<template>
  <div
    ref="fullscreenVisual"
    class="visual"
    :style="{
      backgroundImage: `url(${backgroundImage})`,
      backgroundPosition: 'center',
    }"
    @click="visualTouch()"
  >
    <div class="herader">
      <div v-if="showNavbar">
        <div class="flex-row navbar" v-if="isFullscreen">
          <!-- 新增 -->
          <div class="navbar-basics property-basics" style="margin-left: -7px">
            <div class="flex-between-center basics-box">
              <div
                v-for="(propertybarItem, idx) in propertybarArray"
                :key="idx"
                class="basics-specific c-pointer"
                @click="handlePropertybarClick(propertybarItem, idx)"
              >
                <img
                  class="icon"
                  :class="propertybarItem.text == '人员管理' ? 'renyuan-icon' : ''"
                  :src="propertybarItem.img"
                  alt=""
                />
                <p
                  class="text"
                  :style="{
                    color: currentPropertyIndex === idx ? '#71C8F6' : '',
                  }"
                >
                  {{ propertybarItem.text }}
                </p>
              </div>
            </div>
          </div>
          <div class="navbar-week-plans">
            <div class="navbar-plans">
              <img class="c-pointer plans-image" src="../assets/menu/rili.png" alt="" />
              <p>假期计划</p>
            </div>
            <div class="navbar-plans">
              <img class="c-pointer plans-image" src="../assets/menu/zhileng.png" alt="" />
              <p>制冷模式</p>
            </div>
            <div class="navbar-plans">
              <img class="c-pointer plans-image" src="../assets/menu/zhire.png" alt="" />
              <p>制热模式</p>
            </div>
            <div class="navbar-plans">
              <img class="c-pointer plans-image" src="../assets/menu/guoduji.png" alt="" />
              <p>过渡季模式</p>
            </div>
            <div class="navbar-plans">
              <img class="c-pointer plans-alarm" src="../assets/menu/alarm.png" alt="" />
              <p>报警信息</p>
            </div>
          </div>
          <div class="navbar-weather">
            <div class="navbar-property-layout">
              <div
                class="navbar-basics"
                :class="toolbarData.toolbarCurrentModel.text == '运维模式' ? 'navbar-yunwei' : ''"
              >
                <div class="flex-between-center-yunwei basics-box">
                  <div
                    v-for="(topBasicsItem, idx) in toolbarData.toolbarCurrentModel"
                    :key="idx"
                    class="basics-specific c-pointer"
                    @click="topBasicsClick(topBasicsItem)"
                  >
                    <img :style="getToolsImageStyle(idx)" :src="topBasicsItem.img" alt="" />
                    <p :style="getToolsImageTextStyle(idx)" class="text">
                      {{ topBasicsItem.text }}
                    </p>
                  </div>
                </div>
              </div>
              <div class="flex-column-space navbar-coverage" @click.stop="handleRightFloatClick()">
                <div id="coverage-top-custom-box" class="coverage-top-box">
                  <div class="coverage-bulb">
                    <img class="bulb" src="@/assets/menu/perproty_icon.png" alt="" />
                    <el-popover
                      placement="bottom"
                      width="160"
                      popper-class="custom-popover"
                      trigger="manual"
                      v-model="createGroupVisiable"
                    >
                      <div class="create-group-box">
                        <div class="create-head">
                          <img
                            class="create-group-img"
                            src="../../src/assets/group/luoji.png"
                            alt=""
                          />
                          <p class="create-group-text">创建逻辑组</p>
                        </div>
                        <div class="create-icon">
                          <i class="el-icon-check" @click="createRootTreeClick()"></i>
                          <i class="el-icon-close" @click="closeRootTree()"></i>
                        </div>
                      </div>
                      <el-input
                        style="margin-top: 10px"
                        v-model="createGroupInputValue"
                        placeholder="请输入组名称"
                      ></el-input>
                      <span
                        class="coverage-des"
                        slot="reference"
                        @contextmenu.prevent="showPopover"
                      >
                        页面管理
                      </span>
                    </el-popover>
                  </div>
                  <div class="xiala c-pointer" @click.stop="handleRightFloatClick()">
                    <img
                      class="img"
                      :class="rightFloatView ? '' : 'img-rotate'"
                      src="../assets/visual_images/images/group_xiala2.png"
                      alt=""
                    />
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <router-view />
    <div v-if="mainToggle" class="main">
      <div class="header-area" />
      <div class="flex-row content">
        <div class="flex-column content-box">
          <div class="cad-box">
            <div v-if="showMenu" :style="menuStyle" class="custom-menu" @click="onMenuClick">
              <ul>
                <li
                  style="
                    list-style: none;
                    padding: 5px 6px;
                    font-weight: bold;
                    color: #ccccc1;
                    display: flex;
                    align-items: center;
                    border-bottom: 1px solid #595959;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                    overflow: hidden;
                    max-width: 140px;
                  "
                >
                  {{ selectedObject?.name || '未知模块' }}
                </li>
                <li @click="handleMenuAction('action0')" style="padding: 5px 6px">
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/chuangjian.png"
                      style="width: 15px; height: 15px; margin-right: 5px"
                    />
                    &nbsp;关联数据
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick1 }"
                  @click="handleMenuAction('action0')"
                  style="border-bottom: 1px solid #595959; padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/chuangjian.png"
                      style="width: 15px; height: 15px; margin-right: 4px"
                    />
                    &nbsp;删除关联数据
                  </div>
                </li>
                <li @click="handleMenuAction('action1')" style="padding: 5px 6px">
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/guanlianshuju.png"
                      style="width: 20px; height: 20px; margin-right: 4px"
                    />
                    {{ threeDmenu.canClick2 ? '编辑' : '添加' }}绑定数据
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick2 }"
                  @click="handleMenuAction('action11')"
                  style="border-bottom: 1px solid #595959; padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/guanlianshuju.png"
                      style="width: 18px; height: 18px; margin-right: 4px"
                    />
                    删除绑定数据
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick3 }"
                  @click="handleMenuAction('action2')"
                  style="padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/shebeimuban.png"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    {{ threeDmenu.delete3 ? '编辑' : '添加' }}设备模板
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick31 }"
                  @click="handleMenuAction('action12')"
                  style="border-bottom: 1px solid #595959; padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/shebeimuban.png"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    删除设备模板
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick4 }"
                  @click="handleMenuAction('action3')"
                  style="padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/quanjingshitu.png"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    {{ threeDmenu.delete4 ? '编辑' : '添加' }}全景视图
                  </div>
                </li>
                <li
                  :class="{ 'menu-item': true, disabled: !threeDmenu.canClick41 }"
                  @click="handleMenuAction('action31')"
                  style="border-bottom: 1px solid #595959; padding: 5px 6px"
                >
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/quanjingshitu.png"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    删除全景视图
                  </div>
                </li>
                <!-- // w -->
                <li @click="openThreeLook()" style="padding: 5px 6px">
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      :src="assetMenuIcon"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    {{ assetMenuText }}
                  </div>
                </li>
                <li @click="openZcLook()" style="padding: 5px 6px">
                  <div style="display: flex; align-items: center; font-size: 10px">
                    <img
                      src="../assets/menu/look.png"
                      style="width: 15px; height: 15px; margin-right: 7px"
                    />
                    查看资产
                  </div>
                </li>
                <!-- // w -->
              </ul>
            </div>
            <fulfilling-bouncing-circle-spinner
              v-if="loading"
              id="loading-animation"
              :animation-duration="4000"
              :size="60"
              color="#add8e6"
            />
            <!-- <show-model v-if="!fullOrfloor && showModelVisiable"></show-model> -->
            <div
              id="three-container"
              ref="canvasContainer"
              :style="{
                backgroundImage: 'url(' + treeLeveShowImage + ')',
                backgroundSize: '100% 100%',
              }"
              class="map-container c-pointer"
            ></div>
            <div
              id="map-container"
              :style="{
                backgroundImage: 'url(' + treeLeveShowImage + ')',
                backgroundSize: '100% 100%',
              }"
              v-if="!fullOrfloor && !showModelVisiable"
              class="c-pointer"
            ></div>
            <div class="direction">
              <!-- <p class="public-direction north">北</p>
              <p class="public-direction east">东</p>
              <p class="public-direction south">南</p>
              <p class="public-direction west">西</p> -->
              <!-- 合并能耗代码 begin - - - - -  -->
              <!-- 渲染echarts图 -->
              <div v-for="chart in charts" :key="chart.id">
                <vue-draggable-resizable
                  style="z-index: 1000"
                  class="chart-container"
                  :w="chart.width"
                  :h="chart.height"
                  :x="chart.x"
                  :y="chart.y"
                  @resizing="(x, y, w, h) => onResize(chart.id, w, h)"
                  @dragging="(x, y) => onDrag(chart.id, x, y)"
                  @dragstop="(x, y) => onDragStop(chart.id, x, y)"
                >
                  <div :ref="el => setChartRef(el, chart.id)" class="chart-box"></div>
                  <div class="btn-actions" v-if="currentViewMode == 'editmode'">
                    <i class="el-icon-edit" @click.stop="editChart(chart.id)"></i>
                    <i class="el-icon-close" @click.stop="removeChart(chart.id)"></i>
                    <i class="el-icon-check" @click.stop="saveChart(chart.id)"></i>
                  </div>
                </vue-draggable-resizable>
              </div>
              <!-- 渲染看板图片 -->
              <div
                v-for="image in displayImageArray"
                :key="'image' + image.id"
                @click="handleDisplayImageClick(image)"
              >
                <vue-draggable-resizable
                  style="z-index: 99"
                  class="chart-container"
                  :w="image.width"
                  :h="image.height"
                  :x="Number(image.x)"
                  :y="Number(image.y)"
                  @resizing="(x, y, w, h) => onDisplayImageResize(image.id, w, h)"
                  @dragging="(x, y) => onDisplayImageDrag(image.id, x, y)"
                  @dragstop="(x, y) => onDisplayImageDragStop(image.id, x, y)"
                >
                  <img style="width: 100%; height: 100%" :src="image.url" alt="" />
                  <div class="btn-actions" v-if="currentViewMode == 'editmode'">
                    <i class="el-icon-edit" @click.stop="editDisplayImage(image.id)"></i>
                    <i class="el-icon-close" @click.stop="removeDisplayImage(image.id)"></i>
                    <i class="el-icon-check" @click.stop="saveDisplayImage(image.id)"></i>
                  </div>
                </vue-draggable-resizable>
              </div>
              <!-- 渲染看板文本 -->
              <div
                v-for="text in displayTextArray"
                :key="'text' + text.id"
                @click="handleDisplayTextClick(text)"
              >
                <vue-draggable-resizable
                  style="z-index: 999"
                  class="text-container"
                  :w="1"
                  :h="1"
                  :resizable="false"
                  :x="Number(text.x)"
                  :y="Number(text.y)"
                  @dragging="(x, y) => onDisplayTextDrag(text.id, x, y)"
                  @dragstop="(x, y) => onDisplayTextDragStop(text.id, x, y)"
                >
                  <p
                    class="draggable-text"
                    :style="{
                      fontSize: text.size + 'px',
                      color: text.color,
                      fontWeight: text.fontWeight,
                    }"
                  >
                    {{ text.text }}
                  </p>
                  <div class="btn-actions" v-if="currentViewMode == 'editmode'">
                    <i class="el-icon-edit" @click.stop="editDisplayText(text.id)"></i>
                    <i class="el-icon-close" @click.stop="removeDisplayText(text.id)"></i>
                    <i class="el-icon-check" @click.stop="saveDisplayText(text.id)"></i>
                  </div>
                </vue-draggable-resizable>
              </div>
              <!-- 渲染所有按钮 -->
              <div
                v-for="(btn, index) in buttons"
                :key="index"
                class="draggable-btn"
                :class="{ 'is-hover': currentViewMode === 'operation' }"
                :style="{
                  position: 'absolute',
                  left: btn.x + 'px',
                  top: btn.y + 'px',
                  width: btn.width + 'px',
                  height: btn.height + 'px',
                  cursor: 'move',
                  backgroundImage: btn.fileUrl
                    ? `url(${btn.fileUrl})`
                    : `url(${require('@/assets/btn/' + btn.template + '.png')})`,
                  backgroundSize: 'cover',
                  color: btn.color,
                  fontSize: btn.size + 'px',
                  backgroundSize: '100% 100%',
                  backgroundRepeat: 'no-repeat',
                  cursor: currentViewMode === 'editmode' ? 'move' : 'pointer',
                  zIndex: 999,
                }"
                @mousedown="currentViewMode === 'editmode' && startDrag($event, btn)"
                @click="clickBtn(btn)"
              >
                {{ btn.name }}
                <!-- 右上角操作按钮 -->
                <div class="btn-actions" v-if="currentViewMode == 'editmode'">
                  <i class="el-icon-edit" @click.stop="editBtn(btn)"></i>
                  <i class="el-icon-close" @click.stop="removeBtn(btn, index)"></i>
                  <i class="el-icon-check" @click.stop="saveBtn(btn)"></i>
                </div>
              </div>
              <!-- 添加时间表 -->
              <TimeChart ref="timeChartRef" @add-time-chart="addTimeChart"></TimeChart>
              <div
                v-for="(item, index) in timeCharts"
                :key="index"
                class="time-chart-item"
                :style="{
                  left: item.x + 'px',
                  top: item.y + 'px',
                  position: 'absolute',
                  cursor: currentViewMode === 'editmode' ? 'move' : 'pointer',
                }"
                @mousedown="startDragTime($event, index)"
              >
                <img :src="item.icon" class="time-chart-icon" />
                <!-- 右上角操作按钮 -->
                <div class="btn-actions" v-if="currentViewMode == 'editmode'">
                  <i class="el-icon-edit" @click.stop="editTimeChart(item)"></i>
                  <i class="el-icon-close" @click.stop="removeTimeChart(index)"></i>
                  <i class="el-icon-check" @click.stop="saveTimeChart(item)"></i>
                </div>
              </div>
              <!-- 创建文字 -->
              <div v-for="text in texts" :key="text.id">
                <vue-draggable-resizable
                  :w="text.width"
                  :h="text.height"
                  :x="text.x"
                  :y="text.y"
                  @dragging="(x, y) => onTextDrag(text.id, x, y)"
                >
                  <div class="text-box" :style="{ color: text.color }">{{ text.content }}</div>
                  <button @click="removeText(text.id)" class="delete-btn">删除</button>
                </vue-draggable-resizable>
              </div>
              <!-- 合并能耗代码 end - - - - -  -->
              <!-- <div class="flex-center outer-ring">
                <div class="flex-center inside-ring">
                  <img class="img" :src="isTopView
                    ? require('@/assets/visual_images/images/Xzhou.png')
                    : require('@/assets/visual_images/images/Yzhou.png')
                    " alt="" @click="toggleView()">
                </div>
              </div> -->
            </div>
          </div>
        </div>
        <transition name="slide-fade">
          <div v-show="!rightFloatView" class="property-box-out">
            <div class="group-box" @contextmenu.prevent>
              <!-- 右侧树状目录 -->
              <el-tree
                ref="levelTree"
                v-show="defaultUnfoldTreeLevelDataIndex"
                :data="treeLevelData"
                :expand-on-click-node="false"
                @node-contextmenu="handleRightMenuShowClick"
                @node-click="handleRightNodeClick"
                :props="treeLevelDialogData.defaultProps"
                node-key="id"
                :render-content="renderContent"
                style="min-height: fit-content"
                class="level-tree"
              />
              <!-- 自定义右键菜单 -->
              <div
                v-show="menuVisible"
                class="context-menu"
                :style="{ top: menuY + 'px', left: menuX + 'px' }"
                @mouseenter="handleMouseEnter"
                @mouseleave="handleMouseLeave"
              >
                <div
                  class="menu-item"
                  v-for="(item, index) in groupContextMenu"
                  :key="index"
                  @click="groupContextMenuClick(item)"
                  :style="getMenuItemStyle(item.sign)"
                >
                  <img :src="item.img" :style="getMenuImgStyle(item.sign)" class="menu-img" />
                  <span class="menu-text">{{ item.context }}</span>
                </div>
              </div>
            </div>
            <div class="guide-box" @click="handle3dModelControlVisiable()">
              <img
                :class="{ rotated: !visible3dModelControlVisiable }"
                src="../assets/menu/down.png"
                alt=""
              />
            </div>
            <div v-show="showAngleView && currentViewMode == 'editmode'" class="angleViewBox">
              <angle-view @apply-camera-settings="updateCamera" ref="cameraPanel"></angle-view>
            </div>
          </div>
        </transition>
      </div>
    </div>
    <!-- 树状目录 dialog -->
    <el-dialog
      title=""
      :visible.sync="treeDialog"
      class="el-dialog__header tree-show-class"
      top="18%"
      width="35%"
      :before-close="handleTreeClose"
    >
      <template slot="title">
        <div
          class="addDataBtnBox"
          v-if="this.createDeviceShowCheck"
          @click="handleTreeSureBtnClick()"
        >
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">树状目录</p>
      </template>
      <!-- 做数据联动 -->
      <!-- show-checkbox @check-change="handleTreeDialogCheckChange" -->
      <el-tree
        ref="tree"
        node-key="id"
        :data="treeData"
        style="max-height: 300px; overflow-y: auto; background-color: #252526"
        :props="defaultProps"
        @node-click="handleNodeClick"
      />
    </el-dialog>
    <!-- 绑定数据点位弹窗 dialog -->
    <el-dialog
      title=""
      class="el-dialog__header tree-dialog-class"
      :visible.sync="treeLevelControlVisiable"
      top="18%"
      width="50%"
      :before-close="handleTreeControlClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleBindPointSureClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">绑定数据点位</p>
      </template>
      <el-table
        @selection-change="handleBindPonitChange"
        :data="treeLevelControlData"
        style="width: 100%"
      >
        <el-table-column type="selection" width="55"></el-table-column>
        <el-table-column prop="name" label="数据点位" width="100"></el-table-column>
        <el-table-column prop="sort" label="数值" width="80"></el-table-column>
        <el-table-column prop="unit" label="单位" width="80"></el-table-column>
        <el-table-column prop="type" label="类型" width="120"></el-table-column>
        <el-table-column prop="description" label="描述"></el-table-column>
      </el-table>
    </el-dialog>
    <!-- 添加数据 dialog -->
    <el-dialog
      title=""
      :modal="addDataModalFlag"
      class="el-dialog__header"
      :visible.sync="addCADDataDialog"
      top="17%"
      width="19%"
      :before-close="handleAddCADDataClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddDataClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">添加数据</p>
      </template>
      <el-form :label-position="labelPosition" label-width="60px" :model="addCADDataForm">
        <el-form-item class="choose-data">
          <template #label>
            <span @click="addChooseTextClick" style="cursor: pointer; color: #1890ff">
              选择数据:
            </span>
          </template>
          <el-input
            v-model="addCADDataForm.chooseTreeName"
            size="mini"
            style="width: 100%; height: 10px; outline: none; box-shadow: none"
            class="no-border-input"
          />
        </el-form-item>
        <el-form-item
          style="text-wrap: nowrap"
          label="按钮名称:"
          v-if="addCADDataForm.showEffect == 'picture'"
        >
          <el-input
            v-model="addCADDataForm.picture.btnShowTips"
            size="mini"
            style="width: 100%; height: 10px; outline: none; box-shadow: none"
            class="no-border-input"
          />
        </el-form-item>
        <el-form-item style="text-wrap: nowrap" label="显示效果:">
          <el-radio v-model="addCADDataForm.showEffect" label="numerical">数值</el-radio>
          <el-radio v-if="showAddDataFlag" v-model="addCADDataForm.showEffect" label="picture">
            {{ showEffectText }}
          </el-radio>
          <el-radio v-model="addCADDataForm.showEffect" label="textArea">文本</el-radio>
        </el-form-item>
        <el-row v-if="addCADDataForm.showEffect == 'numerical'">
          <div style="display: flex; text-wrap: nowrap">
            <el-form-item label="数据名称:">
              <el-input
                v-model="addCADDataForm.numerical.designation"
                size="mini"
                style="width: 91px"
              />
            </el-form-item>
            <el-form-item label="数据单位:" style="margin-left: 15px">
              <el-input v-model="addCADDataForm.numerical.unit" size="mini" style="width: 91px" />
            </el-form-item>
          </div>
          <div style="display: flex; text-wrap: nowrap">
            <el-form-item label="字体大小:">
              <el-input
                v-model="addCADDataForm.numerical.fontSize"
                placeholder="11"
                size="mini"
                style="width: 91px"
              />
            </el-form-item>
            <el-form-item label="字体颜色:" style="margin-left: 15px">
              <el-color-picker v-model="addCADDataForm.numerical.fontColor" />
            </el-form-item>
          </div>
        </el-row>
        <el-form-item
          style="text-wrap: nowrap"
          v-else-if="addCADDataForm.showEffect == 'picture'"
          label="按钮样式:"
        >
          <div style="display: flex; justify-content: flex-start; align-items: center">
            <div
              @click="handleAddPanoramaChooseIconClick(index, 'addData')"
              :class="{
                selectedPanoramaIcon: addCADDataForm.picture.status === index,
              }"
              class="panorama-img"
              style="
                width: 40px;
                height: 80px;
                margin-right: 5px;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
              "
              v-for="(item, index) in addPanoramaButtonData.iconArray"
              :key="index"
            >
              <img
                :style="getImageStyle(index)"
                style="margin-bottom: 10px"
                v-for="(imgitem, imgindex) in item"
                :src="imgitem"
                :key="imgindex"
                alt=""
              />
            </div>
          </div>
        </el-form-item>
        <div v-else>
          <div class="addDataText" style="display: flex">
            <el-form-item label="1:" />
            <el-form-item label="文本:" style="margin-left: -35px">
              <el-input
                v-model="addCADDataForm.textArea.two.text"
                size="mini"
                style="width: 65px; margin-left: -25px"
              />
            </el-form-item>
            <el-form-item label="字号:" style="margin-left: 58px">
              <el-input
                v-model="addCADDataForm.textArea.two.fontSize"
                size="mini"
                style="width: 50px; margin-left: -25px"
              />
            </el-form-item>
            <el-form-item label="颜色:" style="margin-left: 42px">
              <el-color-picker
                v-model="addCADDataForm.textArea.two.color"
                style="margin-left: -27px"
              />
            </el-form-item>
            <el-form-item label="闪烁:" style="margin-left: 14px">
              <el-checkbox
                v-model="addCADDataForm.textArea.two.flicker"
                style="margin-left: -27px"
              />
            </el-form-item>
          </div>
          <div class="addDataText" style="display: flex">
            <el-form-item label="0:" />
            <el-form-item label="文本:" style="margin-left: -35px">
              <el-input
                v-model="addCADDataForm.textArea.one.text"
                size="mini"
                style="width: 65px; margin-left: -25px"
              />
            </el-form-item>
            <el-form-item label="字号:" style="margin-left: 58px">
              <el-input
                v-model="addCADDataForm.textArea.one.fontSize"
                size="mini"
                style="width: 50px; margin-left: -25px"
              />
            </el-form-item>
            <el-form-item label="颜色:" style="margin-left: 42px">
              <el-color-picker
                v-model="addCADDataForm.textArea.one.color"
                style="margin-left: -27px"
              />
            </el-form-item>
            <el-form-item label="闪烁:" style="margin-left: 14px">
              <el-checkbox
                v-model="addCADDataForm.textArea.one.flicker"
                style="margin-left: -27px"
              />
            </el-form-item>
          </div>
        </div>
      </el-form>
    </el-dialog>
    <!-- 看板添加数据 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header"
      :visible.sync="boardDataForm.visiable"
      top="17%"
      width="19%"
      :before-close="handleAddBoardDataClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddBoardDataClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">添加数据</p>
      </template>
      <el-form :label-position="labelPosition" label-width="60px" :model="boardDataForm">
        <el-form-item class="choose-data">
          <template #label>
            <span @click="addChooseTextClick" style="cursor: pointer; color: #1890ff">
              选择数据:
            </span>
          </template>
          <el-input
            v-model="boardDataForm.chooseName"
            size="mini"
            style="width: 100%; height: 10px; outline: none; box-shadow: none"
            class="no-border-input"
          />
        </el-form-item>
        <div style="display: flex; text-wrap: nowrap">
          <el-form-item label="基点坐标:">
            <span style="margin-right: 10px">x:</span>
            <el-input v-model="boardDataForm.x" style="width: 60px" placeholder="" />
          </el-form-item>
          <el-form-item label="基点坐标:" style="margin-left: 20px">
            <span style="margin-right: 10px">y:</span>
            <el-input v-model="boardDataForm.y" style="width: 60px" placeholder="" />
          </el-form-item>
        </div>
        <div style="display: flex; text-wrap: nowrap">
          <el-form-item label="数据名称:">
            <el-input v-model="boardDataForm.designation" size="mini" style="width: 91px" />
          </el-form-item>
          <el-form-item label="数据单位:" style="margin-left: 15px">
            <el-input v-model="boardDataForm.unit" size="mini" style="width: 91px" />
          </el-form-item>
        </div>
        <div style="display: flex; text-wrap: nowrap">
          <el-form-item label="字体大小:">
            <el-input
              v-model="boardDataForm.size"
              placeholder="11"
              size="mini"
              style="width: 91px"
            />
          </el-form-item>
          <el-form-item label="字体颜色:" style="margin-left: 15px">
            <el-color-picker v-model="boardDataForm.color" />
          </el-form-item>
        </div>
      </el-form>
    </el-dialog>
    <!-- 显示全景视图自定义dialog -->
    <FloatWindow
      v-model="addCADPanoramaDialog"
      :initialZIndex="500"
      :showConfirmIcon="false"
      :title="panoramaName"
      top="20%"
      width="50%"
      @confirm="handleChooseDeviceTemplateClick"
      @close="handleAddCADPanoramaClose"
    >
      <template #titleIcon>
        <img
          @click="handlePanoramaGetMomeClick()"
          src="../assets/visual_images/r_menu/shuqian.png"
          alt=""
        />
      </template>
      <div class="float-window-show-panorama">
        <div id="viewer" ref="viewer" style="width: 100%; height: 100%" />
      </div>
    </FloatWindow>
    <!-- 备忘录 自定义dialog -->
    <FloatWindow
      v-model="addBookmarkDialog"
      title="备忘录"
      top="20%"
      width="50%"
      @confirm="handleBindMomeBtnClick"
      @close="handleBookmarkClose"
    >
      <div class="mome" style="height: 487px; padding: 0 20px">
        <Tinymce v-model="memoText"></Tinymce>
      </div>
    </FloatWindow>
    <!-- 上传全景图 dialog -->
    <!-- 备忘录 自定义dialog -->
    <FloatWindow
      v-model="addBookmarkPanoramaDialog"
      :title="panoramaNameShowMome"
      top="20%"
      width="50%"
      @confirm="handleBindMomePanoramaBtnClick"
      @close="handleBookmarkPanoramaClose"
    >
      <div class="mome" style="height: 515px; padding: 0 20px">
        <Tinymce v-model="memoPanoramaText"></Tinymce>
      </div>
    </FloatWindow>
    <!-- 上传全景图 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-panorama"
      :visible.sync="uploadCADPanoramaDialog"
      top="20%"
      width="12%"
      :before-close="handleEditCADDataClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handlePanoramaClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">上传全景图</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-panorama-box">
          <p class="upload-text" @click="uploadPanoramaClick()">上传全景图片</p>
          <input
            type="file"
            ref="fileInput"
            accept="image/*"
            @change="handleFileUpload"
            style="display: none"
          />
          <div class="upload-name-box">
            <p class="panorama-name">图片名称：</p>
            <el-input
              v-model="uploadCADPanoramaText"
              size="mini"
              style="width: 120px; margin-left: 5px"
            />
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 全景图添加文本数据 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-panorama"
      :visible.sync="addPanoramaTextDialog"
      top="20%"
      width="13%"
      :before-close="handlePanoramaTextClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddPanoramaTextData()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ operationText }}文本数据</p>
      </template>
      <div class="edit-panorama-body" style="height: 110px">
        <div class="upload-panorama-box">
          <div class="upload-name-box">
            <p class="panorama-name">文本：</p>
            <el-input
              v-model="addPanoramaTextData.text"
              size="mini"
              style="width: 145px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box">
            <p class="panorama-name">链接：</p>
            <el-input
              v-model="addPanoramaTextData.link"
              size="mini"
              style="width: 145px; margin-left: 5px"
            />
          </div>
          <div style="display: flex; justify-content: flex-start; align-items: center">
            <div class="upload-name-box">
              <p class="panorama-name">大小：</p>
              <el-input
                v-model="addPanoramaTextData.fontSize"
                size="mini"
                style="width: 60px; margin-left: 5px"
              />
            </div>
            <div class="upload-name-box" style="margin-left: 20px">
              <p class="panorama-name">颜色：</p>
              <el-color-picker v-model="addPanoramaTextData.fontColor" show-alpha></el-color-picker>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 全景图添加图片 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-panorama"
      :visible.sync="addPanoramaPictrue"
      top="20%"
      width="16%"
      :before-close="handlePanoramaPictrueClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddPanoramaImage()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">添加图片</p>
      </template>
      <div class="edit-panorama-body" style="height: 70px">
        <div class="upload-panorama-box">
          <p
            style="font-size: 11px; color: #1890ff; margin-top: 5px; cursor: pointer"
            @click="handleUploadPanoramaPicture()"
          >
            上传图片
          </p>
          <input
            type="file"
            ref="filePanoramaInput"
            accept="image/*"
            @change="handleFilePanoramaUpload"
            style="display: none"
          />
          <div class="upload-name-box">
            <p class="panorama-name">链接：</p>
            <el-input
              v-model="addPanoramaImagePath"
              size="mini"
              style="width: 220px; margin-left: 5px"
            />
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 全景图添加按钮样式 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header btn-panorama"
      :visible.sync="addPanoramaButtonData.visiable"
      top="16%"
      width="20%"
      :before-close="handlePanoramaButtonClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handlePanoramaAddBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">添加按钮</p>
      </template>
      <div class="edit-panorama-body" style="height: 160px">
        <div class="upload-name-box">
          <p
            class="panorama-name"
            @click="addChooseTextClick"
            style="cursor: pointer; color: #1890ff"
          >
            选择数据
          </p>
          <el-input
            v-model="addPanoramaButtonData.chooseFrontDataName"
            size="mini"
            style="width: 80%; margin-left: 10px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">按钮名称</p>
          <el-input
            v-model="addPanoramaButtonData.description"
            size="mini"
            style="width: 80%; margin-left: 10px"
          />
        </div>
        <div class="upload-name-box" style="margin-top: 12px">
          <p class="panorama-name">按钮样式</p>
          <div
            style="
              display: flex;
              justify-content: space-between;
              align-items: center;
              margin-left: 10px;
            "
          >
            <div
              @click="handleAddPanoramaChooseIconClick(index, 'addPanorama')"
              :class="{
                selectedPanoramaIcon: addPanoramaButtonData.selectIconIndex === index,
              }"
              class="panorama-img"
              style="
                width: 40px;
                height: 80px;
                margin-right: 5px;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
              "
              v-for="(item, index) in addPanoramaButtonData.iconArray"
              :key="index"
            >
              <img
                :style="getImageStyle(index)"
                style="margin-bottom: 10px"
                v-for="(imgitem, imgindex) in item"
                :src="imgitem"
                :key="imgindex"
                alt=""
              />
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 创建空间ID dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-space"
      :visible.sync="createSpaceData.spaceIDDialog"
      top="20%"
      width="19%"
      :before-close="handleSpaceIDDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleSpaceBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">创建空间ID</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box">
          <p class="panorama-name">名称：</p>
          <el-input
            v-model="createSpaceData.spaceIDName"
            size="mini"
            style="width: 100px; margin-left: 5px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">描述：</p>
          <el-input
            v-model="createSpaceData.spaceDescribe"
            size="mini"
            style="width: 250px; margin-left: 5px"
          />
        </div>
        <div
          class="upload-name-box"
          style="width: 180px; display: flex; justify-content: space-between"
        >
          <div style="display: flex">
            <p class="panorama-name">字号：</p>
            <el-input
              v-model="createSpaceData.spaceFontSize"
              size="mini"
              style="width: 70px; margin-left: 5px"
            />
          </div>
          <div style="display: flex; align-items: center">
            <p class="panorama-name">颜色：</p>
            <el-color-picker v-model="createSpaceData.spaceFontColor" />
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 关联数据 dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-relevance"
      :visible.sync="relevanceDialog"
      top="20%"
      width="13%"
      :before-close="handleRelevanceDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleRelevanceBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">关联数据</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box">
          <span class="panorama-bindData" @click="getDataTree()">选择数据</span>
          <span class="panorama-bindData" @click="createQRcode()">生成二维码</span>
        </div>
        <div class="QR-code-box" v-if="QRdata.QRcodeShow">
          <img :src="QRdata.QRcodeImg" alt="" />
        </div>
      </div>
    </el-dialog>

    <!-- 添加空间id资产 dialog -->
    <el-dialog
      title="添加资产"
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="assetDialog"
      top="15%"
      :before-close="handleAssetDialogClose"
      width="25%"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAssetDialogOpen()">
          <i class="el-icon-check" />
        </div>
        <div style="display: flex">
          <p class="dialog-title">资产管理</p>
        </div>
      </template>
      <div class="edit-panorama-body" style="display: flex; flex-direction: column; height: 400px">
        <!-- 表头 -->
        <div class="table-ll">
          <!-- 内容区域：可滚动 -->
          <!-- 表头 -->
          <div style="display: flex; height: 50px; flex-shrink: 0">
            <div style="width: 25%; display: flex; align-items: center; justify-content: center">
              <span>名称</span>
            </div>
            <div style="width: 30%; display: flex; align-items: center; justify-content: center">
              <span>型号</span>
            </div>
            <div style="width: 21%; display: flex; align-items: center; justify-content: center">
              <span>数量</span>
            </div>
            <div style="width: 12%; display: flex; align-items: center; justify-content: center">
              <span>链接</span>
            </div>
            <div style="width: 11%; display: flex; align-items: center; justify-content: center">
              <img
                style="width: 16px; height: 16px; cursor: pointer"
                src="../assets/icon/zengjia.png"
                @click="addAssetRow"
              />
            </div>
          </div>

          <div style="flex: 1">
            <div
              v-for="(item, index) in assetList"
              :key="index"
              style="display: flex; height: 30px; align-items: center"
            >
              <div style="width: 25%; display: flex; justify-content: center">
                <el-input v-model="item.name" size="mini" style="width: 80px" />
              </div>
              <div style="width: 30%; display: flex; justify-content: center">
                <el-input v-model="item.type" size="mini" style="width: 150px" />
              </div>
              <div style="width: 21%; display: flex; justify-content: center">
                <el-input v-model="item.quantity" size="mini" style="width: 50px" />
              </div>
              <div style="width: 12%; display: flex; justify-content: center">
                <img
                  style="width: 16px; height: 16px; cursor: pointer"
                  src="../assets/icon/lianjie.png"
                  @click="openLink(index)"
                />
              </div>
              <div style="width: 11%; display: flex; justify-content: center">
                <img
                  style="width: 16px; height: 16px; cursor: pointer"
                  src="../assets/icon/del.png"
                  @click="removeAssetRow(index)"
                />
              </div>
            </div>
          </div>
        </div>
        <!-- 内容区域 -->
      </div>
      <!-- <span slot="footer" class="dialog-footer">
        <div class="addDataBtnBox" @click="handleAssetBtnClick()">
          <i class="el-icon-check" />
        </div>
      </span> -->
    </el-dialog>

    <!-- 点击空间id链接打开的窗口 -->
    <el-dialog
      title="添加链接"
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="LinkDong"
      top="15%"
      :before-close="handleAssetDialogLink"
      width="20%"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAssetDialogLink()">
          <i class="el-icon-check" />
        </div>
        <div style="display: flex">
          <p class="dialog-title">链接</p>
        </div>
      </template>
      <div
        class="edit-panorama-body"
        style="height: 50%; display: flex; flex-wrap: wrap; margin-bottom: 20px"
      >
        <div class="upload-name-box" style="margin-right: 20px">
          <div style="display: flex">
            <p class="panorama-name">链接：</p>
            <el-input v-model="bianhao" size="mini" style="width: 240px; margin-left: 5px" />
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- 点击空间id查看资产打开的窗口 -->
    <el-dialog
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="LookDong"
      top="15%"
      :before-close="handleAssetDialogLook"
      width="50%"
    >
      <div style="max-height: 500px; overflow-y: auto; padding: 20px">
        <el-table :data="tableData" border class="dialog-table">
          <el-table-column prop="name" label="名称"></el-table-column>
          <el-table-column prop="type" label="型号"></el-table-column>
          <el-table-column prop="quantity" label="数量"></el-table-column>
          <el-table-column prop="link" label="链接">
            <template slot-scope="scope">
              <el-tooltip content="点击复制" placement="top">
                <span
                  style="cursor: pointer; color: #409eff"
                  @click="copyToClipboard(scope.row.link)"
                >
                  {{ scope.row.link }}
                </span>
              </el-tooltip>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-dialog>

    <!-- 添加3D资产 dialog -->
    <el-dialog
      title="添加资产"
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="threesDialog"
      top="15%"
      :before-close="handthreeDialogClose"
      width="30%"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handlethreeDialogOpen()">
          <i class="el-icon-check" />
        </div>
        <div style="display: flex">
          <p class="dialog-title">资产管理</p>
        </div>
      </template>
      <div class="edit-panorama-body" style="display: flex; flex-direction: column; height: 400px">
        <!-- 表头 -->
        <div class="table-ll">
          <!-- 内容区域：可滚动 -->
          <!-- 表头 -->
          <div style="display: flex; height: 50px; flex-shrink: 0">
            <div style="width: 25%; display: flex; align-items: center; justify-content: center">
              <span>模型名称</span>
            </div>
            <div style="width: 25%; display: flex; align-items: center; justify-content: center">
              <span>名称</span>
            </div>
            <div style="width: 30%; display: flex; align-items: center; justify-content: center">
              <span>型号</span>
            </div>
            <div style="width: 21%; display: flex; align-items: center; justify-content: center">
              <span>数量</span>
            </div>
            <div style="width: 12%; display: flex; align-items: center; justify-content: center">
              <span>链接</span>
            </div>
            <div style="width: 11%; display: flex; align-items: center; justify-content: center">
              <img
                style="width: 16px; height: 16px; cursor: pointer"
                src="../assets/icon/zengjia.png"
                @click="addthreeRow"
              />
            </div>
          </div>

          <div style="flex: 1">
            <div
              v-for="(item, index) in threeList"
              :key="index"
              style="display: flex; height: 30px; align-items: center"
            >
              <div style="width: 21%; display: flex; justify-content: center">
                <el-input v-model="item.modelName" size="mini" style="width: 80px" />
              </div>
              <div style="width: 20%; display: flex; justify-content: center">
                <el-input v-model="item.name" size="mini" style="width: 80px" />
              </div>
              <div style="width: 25%; display: flex; justify-content: center">
                <el-input v-model="item.type" size="mini" style="width: 150px" />
              </div>
              <div style="width: 15%; display: flex; justify-content: center">
                <el-input v-model="item.quantity" size="mini" style="width: 50px" />
              </div>
              <div style="width: 10%; display: flex; justify-content: center">
                <img
                  style="width: 16px; height: 16px; cursor: pointer"
                  src="../assets/icon/lianjie.png"
                  @click="openthreeLink(index)"
                />
              </div>
              <div style="width: 9%; display: flex; justify-content: center">
                <img
                  style="width: 16px; height: 16px; cursor: pointer"
                  src="../assets/icon/del.png"
                  @click="removeThreeRow(index)"
                />
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- 点击3D链接打开的窗口 -->
    <el-dialog
      title="添加链接"
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="ThreeDong"
      top="15%"
      :before-close="handleThreeDialogLink"
      width="20%"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleThreeDialogLink()">
          <i class="el-icon-check" />
        </div>
        <div style="display: flex">
          <p class="dialog-title">链接</p>
        </div>
      </template>
      <div
        class="edit-panorama-body"
        style="height: 50%; display: flex; flex-wrap: wrap; margin-bottom: 20px"
      >
        <div class="upload-name-box" style="margin-right: 20px">
          <div style="display: flex">
            <p class="panorama-name">链接：</p>
            <el-input v-model="threehao" size="mini" style="width: 240px; margin-left: 5px" />
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- 点击3D查看资产打开的窗口 -->
    <el-dialog
      :modal="false"
      class="el-dialog__header edit-asset"
      :visible.sync="LookthhreeDong"
      top="15%"
      :before-close="handleThreeDialogLook"
      width="50%"
      height="250"
    >
      <div style="max-height: 500px; overflow-y: auto; padding: 20px">
        <el-table :data="tablehreeData" border class="dialog-table">
          <el-table-column prop="name" label="名称"></el-table-column>
          <el-table-column prop="type" label="型号"></el-table-column>
          <el-table-column prop="quantity" label="数量"></el-table-column>
          <el-table-column prop="link" label="链接">
            <template slot-scope="scope">
              <el-tooltip content="点击复制" placement="top">
                <span
                  style="cursor: pointer; color: #409eff"
                  @click="copyToClipboard(scope.row.link)"
                >
                  {{ scope.row.link }}
                </span>
              </el-tooltip>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-dialog>

    <!-- 新建cad dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header new-cad"
      :visible.sync="saveMapData.saveCADDialog"
      top="20%"
      width="18%"
      :before-close="handleNewCadDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleNewCadBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">新建页面</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box">
          <p class="panorama-name">ID：</p>
          <el-input
            v-model="saveMapData.saveMapId"
            size="mini"
            style="width: 240px; margin-left: 15px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">名称：</p>
          <el-input
            v-model="saveMapData.saveMapName"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">描述：</p>
          <el-input
            v-model="saveMapData.saveMapDes"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
      </div>
    </el-dialog>
    <!-- 修改cad dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header new-cad"
      :visible.sync="updateMapData.updateCADDialog"
      top="20%"
      width="18%"
      :before-close="handleUpdateCadDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleUpdateCadBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">修改图层</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box">
          <p class="panorama-name">ID：</p>
          <el-input
            v-model="updateMapData.updateMapId"
            size="mini"
            style="width: 240px; margin-left: 15px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">名称：</p>
          <el-input
            v-model="updateMapData.updateMapName"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">描述：</p>
          <el-input
            v-model="updateMapData.updateMapDescribe"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
      </div>
    </el-dialog>
    <!-- 删除cad dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header delete-cad"
      :visible.sync="deleteMapData.deleteCADDialog"
      top="20%"
      width="18%"
      :before-close="handleDeleteCadDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleDeleteCadBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">删除图层</p>
      </template>
      <div class="edit-panorama-body">
        <p class="dialog-title">您确定要删除{{ clickCurrentFloor }}图层吗？</p>
      </div>
    </el-dialog>
    <!-- 编辑空间id -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header edit-space"
      :visible.sync="updateSpaceData.updateSpaceIDDialog"
      top="20%"
      width="18%"
      :before-close="handleUpdateSpaceIDDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleUpdateSpaceIDBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">编辑空间ID</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box" style="display: none">
          <p class="panorama-name">ID：</p>
          <el-input
            v-model="updateSpaceData.updateSpaceId"
            size="mini"
            style="width: 240px; margin-left: 15px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">名称：</p>
          <el-input
            v-model="updateSpaceData.updateSpaceName"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">描述：</p>
          <el-input
            v-model="updateSpaceData.updateSpaceDescribe"
            size="mini"
            style="width: 240px; margin-left: 5px"
          />
        </div>
        <div
          class="upload-name-box"
          style="width: 180px; display: flex; justify-content: space-between"
        >
          <div style="display: flex">
            <p class="panorama-name">字号：</p>
            <el-input
              v-model="updateSpaceData.updateSpaceFontSize"
              size="mini"
              style="width: 70px; margin-left: 5px"
            />
          </div>
          <div style="display: flex; align-items: center">
            <p class="panorama-name">颜色：</p>
            <el-color-picker v-model="updateSpaceData.updateSpaceColor" />
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 删除空间id dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header delete-space"
      :visible.sync="deleteSpaceDialog"
      top="20%"
      width="18%"
      :before-close="handleDeleteSpaceDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleDeleteSpaceBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">删除空间ID</p>
      </template>
      <div class="edit-panorama-body">
        <p class="dialog-title">您确定要删除{{ this.currentClickSpaceData.name }}空间ID吗？</p>
      </div>
    </el-dialog>
    <!-- 添加全景视图 -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header add-panorama-dialog"
      :visible.sync="createPanorama.createShowVisiable"
      :close-on-click-modal="false"
      top="20%"
      width="22%"
      :before-close="handleCloseAddPanorama"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddPanorama()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ panoramaDes }}全景视图</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box" v-if="!threeDYemian">
          <p class="panorama-name">选择图标：</p>
          <div
            style="
              width: 35px;
              height: 35px;
              margin-left: 5px;
              display: flex;
              justify-content: center;
              align-items: center;
              cursor: pointer;
            "
            :class="{ selected: createPanorama.createIconIndex === index }"
            @click="selectPanoramaIcon(index)"
            v-for="(item, index) in createPanorama.createIconArray"
            :key="index"
          >
            <img style="width: 20px; height: 20px" :src="item" alt="" />
          </div>
        </div>
        <div class="upload-name-box" style="position: relative">
          <p
            class="panorama-name"
            @click="handleUploadPanorama()"
            style="
              z-index: 99;
              cursor: pointer;
              position: absolute;
              color: #1890ff;
              right: 25px;
              top: 10px;
            "
          >
            {{ panoramaUploadDes }}全景图片
          </p>
          <input
            type="file"
            style="display: none"
            ref="fileInput"
            accept="image/*"
            @change="handleFileUpload"
          />
        </div>
        <div class="upload-name-box">
          <p class="panorama-name">页面名称：</p>
          <el-input
            v-model="createPanorama.createPanoramaName"
            size="mini"
            style="width: 190px; margin-left: 5px"
          />
        </div>
      </div>
    </el-dialog>
    <!-- 添加摄像机 -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header add-camera-dialog"
      :visible.sync="createCamera.createShowVisiable"
      :close-on-click-modal="false"
      top="20%"
      width="22%"
      :before-close="handleCloseAddCamera"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleAddCamera()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">添加摄像机</p>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box">
          <p class="panorama-name">选择图标：</p>
          <div
            style="
              width: 35px;
              height: 35px;
              margin-left: 5px;
              cursor: pointer;
              display: flex;
              justify-content: center;
              align-items: center;
            "
            :class="{ selected: createCamera.createIconIndex === index }"
            @click="selectCameraIcon(index)"
            v-for="(item, index) in createCamera.createIconArray"
            :key="index"
          >
            <img style="width: 20px; height: 20px; cursor: pointer" :src="item" alt="" />
          </div>
        </div>
        <div class="upload-name-box" style="position: relative">
          <p class="panorama-name">相机编号：</p>
          <el-input
            v-model="createCamera.createCameraSerial"
            size="mini"
            style="width: 230px; margin-left: 5px"
          />
        </div>
        <div class="upload-name-box" style="position: relative">
          <p class="panorama-name">相机地址：</p>
          <el-input
            v-model="createCamera.createCameraPath"
            size="mini"
            style="width: 230px; margin-left: 5px"
          />
        </div>
      </div>
    </el-dialog>
    <!-- 添加设备模板 -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header add-device-dialog"
      :visible.sync="createDevice.createShowVisiable"
      :close-on-click-modal="false"
      top="20%"
      width="22%"
      :before-close="handleCloseAddDevice"
    >
      <template slot="title">
        <p class="dialog-title">添加设备模板</p>
        <div class="addDataBtnBox" @click="handleAddDevice()">
          <i class="el-icon-check" />
        </div>
      </template>
      <div class="edit-panorama-body">
        <div class="upload-name-box" v-if="!threeDYemian">
          <p class="panorama-name">选择图标：</p>
          <div
            style="
              width: 35px;
              height: 35px;
              margin-left: 5px;
              cursor: pointer;
              display: flex;
              justify-content: center;
              align-items: center;
            "
            :class="{ selected: createDevice.createIconIndex === index }"
            @click="selectDeviceIcon(index)"
            v-for="(item, index) in createDevice.createIconArray"
            :key="index"
          >
            <img style="width: 20px; height: 20px; cursor: pointer" :src="item" alt="" />
          </div>
        </div>
        <div class="upload-name-box" style="position: relative">
          <p
            class="panorama-name"
            style="color: #1890ff; cursor: pointer"
            @click="handleAddDeviceChooseIcu()"
          >
            选择设备
          </p>
          <p
            class="panorama-name"
            style="color: #1890ff; cursor: pointer; margin-left: 50px"
            @click="handleAddDeviceTemplate()"
          >
            选择模板
          </p>
        </div>
      </div>
    </el-dialog>
    <FloatWindow
      v-model="dialogVisible"
      title="运维办公室监控视频"
      top="20%"
      width="50%"
      :showConfirmIcon="false"
      @close="handleCameraViewClose"
    >
      <div class="float-window-iframe-panorama-template">
        <webview
          v-if="dialogVisible"
          ref="cameraView"
          id="cameraView"
          :src="createCamera.createCameraPath"
          style="width: 100%; height: 100%; position: absolute; z-index: 1"
          allowpopups
        ></webview>
      </div>
    </FloatWindow>
    <!-- iframe全景图画面dialog -->
    <el-dialog
      :visible.sync="dialogVisible2"
      class="panorama"
      :title="selectedObjectName"
      width="40%"
      top="14%"
      @close="handleCameraViewClose2"
    >
      <p>{{ selectedObjectName }} 的详细信息</p>
      <div class="panorama-body">
        <div id="viewerThree" ref="viewerThree" style="width: 100%; height: 100%" />
      </div>
    </el-dialog>
    <!-- 添加右侧treedialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header right-tree"
      :visible.sync="treeLevelDialogData.treeDialogVisiable"
      top="20%"
      width="18%"
      :before-close="handleRightTreeClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleRightMenuClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ treeLevelDialogData.treeMenuText }}</p>
      </template>
      <div class="edit-panorama-body">
        <!-- 添加逻辑组 -->
        <div class="upload-name-box" v-if="treeLevelDialogData.treeMenuType == 'group'">
          <p class="panorama-name">逻辑组：</p>
          <el-input
            v-model="treeLevelDialogData.treeMenuGroup.text"
            size="mini"
            style="width: 230px; margin-left: 5px"
          />
        </div>
        <!-- 添加3D页面 -->
        <div v-else-if="treeLevelDialogData.treeMenuType == 'threeD'">
          <div class="upload-name-box">
            <p class="panorama-name">页面名称：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuThreeD.text"
              size="mini"
              style="width: 210px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box">
            <p class="panorama-name">文件名称：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuThreeD.path"
              size="mini"
              style="width: 210px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box" style="display: none">
            <p class="panorama-name">相机坐标：</p>
            <div
              class="camera-coordinate-box"
              style="display: flex; justify-content: space-between; align-items: center"
            >
              <div class="camera-x" style="margin-left: 5px">
                <p class="panorama-name">X：</p>
                <el-input
                  v-model="treeLevelDialogData.treeMenuThreeD.cameraAxis.x"
                  size="mini"
                  style="width: 20px; margin-left: 5px"
                />
              </div>
              <div class="camera-y" style="margin-left: 35px">
                <p class="panorama-name">Y：</p>
                <el-input
                  v-model="treeLevelDialogData.treeMenuThreeD.cameraAxis.y"
                  size="mini"
                  style="width: 20px; margin-left: 5px"
                />
              </div>
              <div class="camera-y" style="margin-left: 35px">
                <p class="panorama-name">Z：</p>
                <el-input
                  v-model="treeLevelDialogData.treeMenuThreeD.cameraAxis.z"
                  size="mini"
                  style="width: 20px; margin-left: 5px"
                />
              </div>
            </div>
          </div>
        </div>
        <!-- 添加CAD页面 -->
        <div v-else-if="treeLevelDialogData.treeMenuType == 'cad'">
          <div class="upload-name-box">
            <p class="panorama-name">页面名称：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuCad.text"
              size="mini"
              style="width: 220px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box">
            <p class="panorama-name">mapID：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuCad.mapID"
              size="mini"
              style="width: 240px; margin-left: 5px"
            />
          </div>
        </div>
        <!-- 添加图片页面 -->
        <div v-else-if="treeLevelDialogData.treeMenuType == 'image'">
          <div class="upload-name-box">
            <p class="panorama-name">图片名称：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuImage.text"
              size="mini"
              style="width: 220px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box" @click="triggerFileSelect()">
            <p class="panorama-name" style="color: #1890ff; cursor: pointer">上传图片</p>
          </div>
          <input
            type="file"
            accept="image/*"
            ref="fileInput"
            style="display: none"
            @change="handleFileChange"
          />
        </div>
        <!-- 添加链接 -->
        <div v-else>
          <div class="upload-name-box">
            <p class="panorama-name">链接名称：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuLink.text"
              size="mini"
              style="width: 230px; margin-left: 5px"
            />
          </div>
          <div class="upload-name-box">
            <p class="panorama-name">链接地址：</p>
            <el-input
              v-model="treeLevelDialogData.treeMenuLink.path"
              size="mini"
              style="width: 230px; margin-left: 5px"
            />
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 删除 right tree dialog -->
    <el-dialog
      title=""
      :modal="false"
      class="el-dialog__header delete-tree-space"
      :visible.sync="treeDialogDeleteVisiable"
      top="20%"
      width="18%"
      :before-close="handleDeleteTreeDialogClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" @click="handleDeleteTreeBtnClick()">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">删除</p>
      </template>
      <div class="edit-panorama-body">
        <p class="dialog-title">您确定要删除{{ currentTreeNode?.name }}吗？</p>
      </div>
    </el-dialog>
    <!-- 展示设备模板的自定义dialog -->
    <FloatWindow
      v-model="deviceImageVisiable"
      :initialZIndex="5000"
      title="设备模板"
      top="20%"
      width="50%"
      @confirm="handleChooseDeviceTemplateClick"
      @close="handleDeviceImageVisiable"
    >
      <div class="float-window-device-template">
        <div
          class="img-box"
          v-for="(item, index) in createDevicePictureArr"
          :key="index"
          @click="selectDeviceImage(index)"
        >
          <div class="checkbox" v-if="createDeviceSelectedImageIndex === index">
            <i class="el-icon-check"></i>
          </div>
          <img :src="item" alt="" style="width: 100%; height: 100%" />
        </div>
      </div>
    </FloatWindow>
    <fan-dialog ref="fan"></fan-dialog>
    <!-- 合并能耗代码 begin - - - - -  -->
    <!-- 上传背景图片的对话框 -->
    <el-dialog
      title=""
      class="el-dialog__header upload-bg"
      :visible.sync="uploadDialogVisible"
      top="18%"
      width="22%"
      :before-close="handleCloseUpload"
    >
      <template slot="title">
        <div class="addDataBtnBox" style="z-index: 999; cursor: pointer">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ title }}</p>
      </template>
      <el-upload
        class="upload-box"
        action=""
        :show-file-list="false"
        :before-upload="handleUpload"
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将图片拖拽到此处，或
          <em>点击上传</em>
        </div>
      </el-upload>
    </el-dialog>
    <!--  饼状图 -->
    <el-dialog
      title=""
      :visible.sync="energyModelPie"
      top="17%"
      width="24%"
      :before-close="handleClosePie"
    >
      <template slot="title">
        <div class="addDataBtnBox" style="z-index: 999; cursor: pointer" @click="confirmPieChart">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ title }}</p>
      </template>
      <div class="item-box">
        <span style="margin-right: 10px">看板名称:</span>
        <div style="width: 100%">
          <el-input v-model="customPiePostFloor" placeholder="请输入看板名称" />
        </div>
        <span style="margin-left: 10px">名称颜色:</span>
        <div style="margin-left: 5px">
          <el-color-picker v-model="customPiePostNameBackgroundColor" show-alpha></el-color-picker>
        </div>
        <span style="margin-left: 10px">背景颜色:</span>
        <div style="margin-left: 5px">
          <el-color-picker v-model="customPiePostBackgroundColor" show-alpha></el-color-picker>
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">基点坐标</span>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <span style="margin-right: 10px">x:</span>
          <div class="inp-box" style="width: 70%">
            <el-input v-model="customPiePostX" placeholder="请输入X坐标" />
          </div>
        </div>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <span style="margin-right: 10px">y:</span>
          <div class="inp-box" style="width: 70%">
            <el-input v-model="customPiePostY" placeholder="请输入Y坐标" />
          </div>
        </div>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <span style="margin-right: 10px">宽度:</span>
          <div class="inp-box" style="width: 70%">
            <el-input v-model="customPiePostWidth" placeholder="请输入宽度" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 10px"
        >
          <span style="margin-right: 10px">高度:</span>
          <div class="inp-box" style="width: 70%">
            <el-input v-model="customPiePostHeight" placeholder="请输入高度" />
          </div>
        </div>
      </div>
      <div
        style="
          font-size: 11px;
          color: #1890ff;
          margin-top: 5px;
          margin-bottom: 15px;
          cursor: pointer;
        "
        @click="addPieChartData"
      >
        选择数据
      </div>
      <div class="data-box">
        <el-table
          :data="pieChartData"
          style="width: 100%"
          size="mini"
          element-loading-background="rgba(0, 0, 0, 0)"
          :stripe="false"
          :height="200"
          border
          :header-cell-style="{
            background: '#252526',
            color: '#c7c7c9',
            border: '1px solid #4e4c4c',
          }"
        >
          <el-table-column label="名称" prop="name" header-align="center">
            <template slot-scope="scope">
              <el-input v-model="scope.row.name" placeholder="请输入名称"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="颜色" prop="color" width="60" header-align="center">
            <template slot-scope="scope">
              <el-color-picker v-model="scope.row.color" show-alpha></el-color-picker>
            </template>
          </el-table-column>
          <el-table-column label="按钮" width="80" header-align="center">
            <template slot-scope="scope">
              <el-button type="primary" size="mini" @click="handlePieDataRowDelete(scope.row.id)">
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-dialog>
    <!-- 柱状图 -->
    <el-dialog
      title=""
      class="el-dialog__header upload-histogram"
      :visible.sync="energyModel"
      top="16%"
      width="24%"
      :before-close="handleClose"
    >
      <template slot="title">
        <div class="addDataBtnBox" style="z-index: 999; cursor: pointer" @click="confirmBarChart">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ title }}</p>
      </template>
      <div class="item-box">
        <span style="margin-right: 10px">看板名称:</span>
        <div class="inp-box" style="width: 90%">
          <el-input v-model="customBarPostFloor" placeholder="请输入看板名称" />
        </div>
        <span style="margin-left: 10px">名称颜色:</span>
        <div style="margin-left: 5px">
          <el-color-picker v-model="customBarPostNameBackgroundColor" show-alpha></el-color-picker>
        </div>
        <span style="margin-left: 10px">背景颜色:</span>
        <div style="margin-left: 5px">
          <el-color-picker v-model="customBarPostBackgroundColor" show-alpha></el-color-picker>
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">Y轴</span>
        <span style="margin-right: 10px">最大值:</span>
        <div style="width: 65px">
          <el-input v-model="yAxisMaxBar" placeholder="" />
        </div>
        <span style="margin-right: 10px; margin-left: 20px">最小值:</span>
        <div style="width: 65px">
          <el-input v-model="yAxisMinBar" placeholder="" />
        </div>
        <span style="margin-left: 20px; margin-right: 10px">单位:</span>
        <div style="width: 50px">
          <el-input v-model="yAxisUnitBar" placeholder="" />
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">X轴</span>
        <span style="margin-right: 10px">时间范围:</span>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <div style="width: 100px">
            <el-input v-model="barChartHours" placeholder="多少小时之内" />
          </div>
          <p style="font-size: 11px; color: #faad14; margin-left: 10px">小时</p>
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">基点坐标</span>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <span style="margin-right: 10px">x:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customBarPostX" placeholder="请输入X坐标" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">y:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customBarPostY" placeholder="请输入Y坐标" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">宽度:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customBarPostWidth" placeholder="请输入宽度" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">高度:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customBarPostHeight" placeholder="请输入高度" />
          </div>
        </div>
      </div>
      <div
        style="font-size: 11px; color: #1890ff; margin-top: 5px; cursor: pointer"
        @click="addBarChartData"
      >
        选择数据
      </div>
      <div class="data-box">
        <el-table
          :data="barChartData"
          style="width: 100%"
          size="mini"
          element-loading-background="rgba(0, 0, 0, 0)"
          :stripe="false"
          :height="180"
          border
          :header-cell-style="{
            background: '#252526',
            color: '#c7c7c9',
            border: '1px solid #4e4c4c',
          }"
        >
          <el-table-column label="名称" prop="name" align="center" header-align="center">
            <template slot-scope="scope">
              <el-input v-model="scope.row.name" placeholder="请输入名称"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="颜色" prop="color" width="50" header-align="center">
            <template slot-scope="scope">
              <el-color-picker v-model="scope.row.color" show-alpha></el-color-picker>
            </template>
          </el-table-column>
          <el-table-column label="按钮" width="80" header-align="center">
            <template slot-scope="scope">
              <el-button type="primary" size="mini" @click="handleBarDataRowDelete(scope.row.id)">
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-dialog>
    <!-- 曲线图 -->
    <el-dialog
      title=""
      :visible.sync="energyModelLine"
      top="15%"
      width="24%"
      :before-close="handleCloseLine"
    >
      <template slot="title">
        <div class="addDataBtnBox" style="z-index: 999; cursor: pointer" @click="confirmLineChart">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ title }}</p>
      </template>
      <div class="item-box">
        <span style="margin-right: 10px">看板名称:</span>
        <div class="inp-box" style="width: 120px">
          <el-input v-model="customLinePostFloor" placeholder="请输入名称" />
        </div>
        <span style="margin-right: 10px; margin-left: 20px">类型:</span>
        <div style="width: 200px; display: flex">
          <div style="width: 100px">
            <el-radio v-model="radio" label="log">趋势数据</el-radio>
          </div>
          <div style="width: 100px">
            <el-radio v-model="radio" label="realtime">实时数据</el-radio>
          </div>
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">Y轴</span>
        <span style="margin-right: 10px">最大值:</span>
        <div style="width: 65px">
          <el-input v-model="yAxisMaxLine" placeholder="" />
        </div>
        <span style="margin-right: 10px; margin-left: 20px">最小值:</span>
        <div style="width: 65px">
          <el-input v-model="yAxisMinLine" placeholder="" />
        </div>
        <span style="margin-left: 20px; margin-right: 10px">单位:</span>
        <div style="width: 50px">
          <el-input v-model="yAxisUnitLine" placeholder="" />
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">X轴</span>
        <span style="margin-right: 10px">时间范围:</span>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <div style="width: 60px">
            <el-input v-model="lineChartHours" placeholder="" />
          </div>
          <p style="font-size: 11px; color: #faad14; margin-left: 10px">小时</p>
        </div>
        <span style="margin-left: 10px">名称颜色:</span>
        <div style="margin-left: 10px">
          <el-color-picker v-model="customLinePostNameBackgroundColor" show-alpha></el-color-picker>
        </div>
        <span style="margin-left: 10px">背景颜色:</span>
        <div style="margin-left: 10px">
          <el-color-picker v-model="customLinePostBackgroundColor" show-alpha></el-color-picker>
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px">基点坐标</span>
        <div style="display: flex; justify-content: flex-start; align-items: center">
          <span style="margin-right: 10px">x:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customLinePostX" placeholder="" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">y:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customLinePostY" placeholder="" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">宽度:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customLinePostWidth" placeholder="" />
          </div>
        </div>
        <div
          style="display: flex; justify-content: flex-start; align-items: center; margin-left: 5px"
        >
          <span style="margin-right: 10px">高度:</span>
          <div class="inp-box" style="width: 55px">
            <el-input v-model="customLinePostHeight" placeholder="" />
          </div>
        </div>
      </div>
      <div
        style="font-size: 11px; color: #1890ff; margin-top: 5px; cursor: pointer"
        @click="addLineChartData"
      >
        选择数据
      </div>
      <div class="data-box">
        <el-table
          :data="lineChartData"
          style="width: 100%"
          size="mini"
          element-loading-background="rgba(0, 0, 0, 0)"
          :stripe="false"
          :height="180"
          border
          :header-cell-style="{
            background: '#252526',
            color: '#c7c7c9',
            border: '1px solid #4e4c4c',
          }"
        >
          <el-table-column label="名称" prop="name" header-align="center">
            <template slot-scope="scope">
              <el-input v-model="scope.row.name" placeholder="请输入名称"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="颜色" prop="color" width="60" header-align="center">
            <template slot-scope="scope">
              <el-color-picker v-model="scope.row.color" show-alpha></el-color-picker>
            </template>
          </el-table-column>
          <el-table-column label="底色" prop="color" width="60" header-align="center">
            <template slot-scope="scope">
              <el-color-picker v-model="scope.row.lineBackgroundColor" show-alpha></el-color-picker>
            </template>
          </el-table-column>
          <el-table-column label="按钮" width="80" header-align="center">
            <template slot-scope="scope">
              <el-button type="primary" size="mini" @click="handleLineDataRowDelete(scope.row.id)">
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-dialog>
    <!-- 文本框 -->
    <el-dialog
      title=""
      :visible.sync="boardTextForm.visiable"
      top="20%"
      width="16%"
      :before-close="handleCloseText"
    >
      <template slot="title">
        <div class="addDataBtnBox" style="z-index: 999; cursor: pointer" @click="addText">
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">{{ title }}</p>
      </template>
      <div class="item-box">
        <span style="margin-right: 10px; text-wrap: nowrap">内容:</span>
        <div class="inp-box" style="width: 90%">
          <el-input v-model="boardTextForm.text" placeholder="请输入内容" />
        </div>
      </div>
      <div class="item-box">
        <span style="margin-right: 10px; text-wrap: nowrap">链接:</span>
        <div class="inp-box" style="width: 90%">
          <el-input v-model="boardTextForm.link" placeholder="请填写链接" />
        </div>
      </div>
      <div class="item-box">
        <div style="display: flex; align-items: center; justify-content: flex-start">
          <span style="margin-right: 10px; text-wrap: nowrap">字号:</span>
          <div class="inp-box" style="width: 50px">
            <el-input v-model="boardTextForm.size" placeholder="请输入字号" />
          </div>
        </div>
        <div
          style="margin-left: 10px; display: flex; align-items: center; justify-content: flex-start"
        >
          <span style="margin-right: 10px; text-wrap: nowrap">加粗:</span>
          <div class="inp-box" style="width: 50px">
            <el-input v-model="boardTextForm.bold" placeholder="" />
          </div>
        </div>
        <div
          style="margin-left: 10px; display: flex; align-items: center; justify-content: flex-start"
        >
          <span style="margin-right: 10px">颜色:</span>
          <el-color-picker v-model="boardTextForm.color"></el-color-picker>
        </div>
      </div>
      <div style="display: flex; text-wrap: nowrap; margin-bottom: 8px">
        <div>
          <span style="margin-left: 5px; margin-right: 5px">基点坐标 x:</span>
          <el-input v-model="boardTextForm.x" style="width: 60px" placeholder="" />
        </div>
        <div>
          <span style="margin-left: 10px; margin-right: 10px">y:</span>
          <el-input v-model="boardTextForm.y" style="width: 60px" placeholder="" />
        </div>
      </div>
    </el-dialog>
    <!-- 3D模型弹出窗 -->
    <ThreeDdialog
      ref="threeDialog"
      :modelNameProp="selectedModelName"
      @threePanoramaDialog="threeDModelOpenPanorama"
      @threeDeviceDialog="threeDModelDevice"
      @threeSelectData="selectThreeDTreeData"
    ></ThreeDdialog>
    <!-- 超链接 -->
    <!-- 合并能耗代码 begin - - - - -  -->
    <!-- 3D模型绑定数据 -->
    <el-dialog
      title=""
      :visible.sync="threeDDataBindingShow"
      top="20%"
      width="20%"
      :before-close="threeDDataBindingShowClose"
    >
      <template slot="title">
        <div
          class="addDataBtnBox"
          style="z-index: 999; cursor: pointer"
          @click="threeDDataBindingOk"
        >
          <i class="el-icon-check" />
        </div>
        <p class="dialog-title">绑定数据</p>
      </template>
      <el-form :label-position="labelPosition" label-width="80px" :model="addCADDataForm">
        <el-form-item class="choose-data">
          <template #label>
            <span @click="addChooseTextClick" style="cursor: pointer; color: #1890ff">
              选择数据:
            </span>
          </template>
          <el-input
            v-model="addCADDataForm.chooseTreeName"
            size="mini"
            style="width: 100%; height: 10px; outline: none; box-shadow: none"
            class="no-border-input"
          />
        </el-form-item>
      </el-form>
      <!-- 最低值范围 -->
      <div class="config-item">
        <span>最低值范围&nbsp;&nbsp;：</span>
        <div style="width: 80px; margin-left: 5px">
          <el-input v-model="threeDFormData.low.min" size="small" />
        </div>
        ~
        <div style="width: 80px"><el-input v-model="threeDFormData.low.max" size="small" /></div>
        <el-color-picker v-model="threeDFormData.low.color" size="small" />
      </div>

      <!-- 最合适值范围 -->
      <div class="config-item">
        <span>最合适值范围：</span>
        <div style="width: 80px"><el-input v-model="threeDFormData.mid.min" size="small" /></div>
        ~
        <div style="width: 80px"><el-input v-model="threeDFormData.mid.max" size="small" /></div>
        <el-color-picker v-model="threeDFormData.mid.color" size="small" />
      </div>

      <!-- 最高值范围 -->
      <div class="config-item">
        <span>最高值范围&nbsp;&nbsp;：</span>
        <div style="width: 80px; margin-left: 5px">
          <el-input v-model="threeDFormData.high.min" size="small" />
        </div>
        ~
        <div style="width: 80px"><el-input v-model="threeDFormData.high.max" size="small" /></div>
        <el-color-picker v-model="threeDFormData.high.color" size="small" />
      </div>
    </el-dialog>
    <AddButton
      :visible.sync="showAddButtonDialog"
      @confirm="onConfirmAddButton"
      @add-btn="addButton"
      ref="addbtnbox"
      @select-btn="selectBtn"
    ></AddButton>
    <AddImage
      ref="displayAddImage"
      :cadTreeId="treeLevelCurrentId"
      @add-display-image-success="handleDisplayImageSuccess"
    ></AddImage>
    <TopDialog ref="topdialog"></TopDialog>
    <LeftDialog ref="leftDialog">
      <div class="energy-close-box">
        <p class="energy-close" @click="handleEnergyCloseClick()">X</p>
      </div>
      <div v-if="leftDialogShowContent == '能耗'" class="energy-content">
        <div class="tem-device" @click="deviceClickTest()">
          <p>设备健康状况</p>
          <div class="device-box1">
            <p>照明 95</p>
            <p>空调 88</p>
          </div>
          <div class="device-box2">
            <p>供水 85</p>
            <p>物联 90</p>
          </div>
        </div>
        <div class="tem-system" @click="systemClickTest()">
          <p>系统健康状况</p>
          <div class="system-box1">
            <p>照明系统 95</p>
            <p>空调系统 97</p>
          </div>
        </div>
        <div class="tem-environment" @click="environmentClickTest()">
          <p>环境健康状况</p>
          <div class="environment-box1">
            <p>湿度 95</p>
            <p>温度 88</p>
          </div>
          <div class="environment-box2">
            <p>CO2 98</p>
            <p>VOC 97</p>
          </div>
        </div>
      </div>
      <div v-if="leftDialogShowContent == 'showDevice'">
        <el-tree
          :data="showDeviceData"
          :props="testDefaultProps"
          accordion
          @node-click="handleEnergyNodeClick"
          :render-content="renderTreeNode"
        >
          <span slot-scope="{ node, data }" class="tree-node">
            <span class="tree-label">{{ data.label }}</span>
            <span v-if="data.extra" class="tree-extra">{{ data.extra }}</span>
          </span>
        </el-tree>

        <!-- 右键菜单 -->
        <div
          v-show="menuVisible"
          class="context-menu"
          :style="{ top: menuY + 'px', left: menuX + 'px' }"
          @click="menuVisible = false"
        >
          <ul>
            <li @click="handleSetThreshold">设置阀值</li>
          </ul>
        </div>
      </div>

      <div v-if="leftDialogShowContent == '分数'">
        <div class="target-device">
          <div class="tem-target-device" @click="deviceTargetClickTest()">
            <p>能源指标</p>
            <img src="../assets/systemImage/nengxiao.png" alt="" />
            <span class="tem-target-span">997</span>
          </div>
          <div class="target-line"></div>
          <div class="tem-target-system" @click="systemTargetClickTest()">
            <p>运行指标</p>
            <img src="../assets/systemImage/yujing.png" alt="" />
            <span class="tem-target-span">1557</span>
          </div>
          <div class="target-line"></div>
          <div class="tem-target-environment" @click="environmentTargetClickTest()">
            <p>能效水平</p>
            <img src="../assets/systemImage/cilun.png" alt="" />
            <span class="tem-target-span">1201</span>
          </div>
        </div>
        <div class="targetShowTree" v-if="leftDialogTargetShowTree">
          <el-tree
            :data="showDeviceData"
            :props="testDefaultProps"
            accordion
            @node-click="handleEnergyNodeClick"
          >
            <!-- 自定义节点内容 -->
            <span slot-scope="{ node, data }" class="tree-node">
              <span class="tree-label">{{ data.label }}</span>
              <span v-if="data.extra" class="tree-extra">{{ data.extra }}</span>
            </span>
          </el-tree>
        </div>
        <div class="targetShowDetails" v-if="leftDialogTargetShowDetail">
          <div class="section">
            <h2>舞台区</h2>
            <p>主持人和嘉宾主要活动区域，背景为LED大屏。</p>
          </div>

          <div class="section">
            <h2>观众区</h2>
            <p>设置观众座椅，可根据节目需要灵活调整。</p>
          </div>

          <div class="section">
            <h2>控制室</h2>
            <p>包含调音台、切换台、录制设备，负责节目制作控制。</p>
          </div>

          <div class="section">
            <h2>灯光与音响</h2>
            <p>舞台灯光：柔光、聚光。</p>
            <p>音响：无线话筒、返送音箱。</p>
          </div>

          <div class="section">
            <h2>摄像机位</h2>
            <p>正面机位、侧面机位、轨道机位，覆盖多角度拍摄。</p>
          </div>
        </div>
      </div>
    </LeftDialog>
    <!-- 报警模块 -->
    <FloatWindow
      v-model="alarmModuleVisiable"
      title="报警模块"
      top="20%"
      width="70%"
      :showConfirmIcon="false"
      :showCustomAlarm="true"
      @close="handleAlarmClose"
      @alarm="handleCustomAlarm"
    >
      <el-table :data="alarmTableData" style="width: 100%" height="700">
        <el-table-column fixed prop="status" label="报警状态" width="70"></el-table-column>
        <el-table-column prop="limit" label="累计次数" width="70"></el-table-column>
        <el-table-column prop="lasttime" label="最后一次报警时间" width="120"></el-table-column>
        <el-table-column prop="alarmObject" label="报警对象" width="350"></el-table-column>
        <el-table-column prop="alarmDes" label="报警描述" width="160"></el-table-column>
        <el-table-column prop="triggerTime" label="触发时间" width="120"></el-table-column>
        <el-table-column prop="SendWork" label="工单发送" width="120"></el-table-column>
        <el-table-column prop="operationUser" label="操作用户" width="60"></el-table-column>
        <el-table-column fixed="right" label="操作" width="150">
          <template slot-scope="scope">
            <el-button @click="handleClick(scope.row)" type="text" size="small">派送报警</el-button>
            <el-button type="text" size="small">查看报警</el-button>
          </template>
        </el-table-column>
      </el-table>
    </FloatWindow>
  </div>
</template>

<script>
import axios from 'axios';
import vjmap from 'vjmap';
import * as THREE from 'three';
import 'vjmap/dist/vjmap.min.css';
import 'vjmap3d/dist/vjmap3d.min.css';
import setRem from '../../rem.js';
import { vue3dLoader } from 'vue-3d-loader';
import { FulfillingBouncingCircleSpinner } from 'epic-spinners';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { Viewer } from 'photo-sphere-viewer';
import { MarkersPlugin } from 'photo-sphere-viewer/dist/plugins/markers';
import 'photo-sphere-viewer/dist/photo-sphere-viewer.css';
import 'photo-sphere-viewer/dist/plugins/markers.css';
import screenfull from 'screenfull';

const { ipcRenderer } = window.require('electron');
import Tinymce from '../components/Tinymce/index.vue';
import FanDialog from './dialog/fanDialog.vue';
import FloatWindow from '../components/FloatWindow/FloatWindow.vue';
import AngleView from './dialog/AngleView.vue';
import ThreeDdialog from './dialog/threeDdialog.vue';
import AddButton from './dialog/addButton';
import AddImage from './dialog/addImage.vue';
import TimeChart from './dialog/timeChart';
import TopDialog from './dialog/topDialog';
import LeftDialog from './dialog/leftDialog';
import showModel from './dialog/showModel';
import { spaceProperty, spaProperty, byProperty, cadProperty } from '../api/api.js';
import {
  saveCAD,
  deleteCADById,
  updateCAD,
  getSpaceById,
  saveSpace,
  updateSpace,
  deleteSpaceById,
  createQRcode,
  getSpaceDataByQRcode, // 扫描二维码显示数据
  createAddData,
  getDataByCADId,
  deleteData,
  linkData,
  createBindMome,
  getMome,
  createBindMomeInPanorama,
  getMomeInPanorama,
  getDataTree,
  getDataTreeById,
  getRealDataPoint, // 获取实时数据
  savePanorama,
  deletePanorama,
  getPanoramaByCadId,
  postPanoramaAddmaker,
  getPanoramaAddVideo,
  postPanoramaAddVideo,
  deletePanoramaVideo,
  getPanoramaDataBySpaceId,
  deletePanoramaData,
  createRightTree,
  updateRightTree,
  deleteRightTree,
  getRightTree,
  createDisplay,
  getDisplayByCadTree,
  deleteDisplay,
  threeDBindData,
  threeDQueryBindData,
  deleteThreeDQueryBindData,
  deviceTemplate,
  selectDeviceTemplate,
  deletedeviceTemplate,
  createThreeDQRCode,
  savePageLink,
  selectPageLink,
  delPageLink,
  getDisplayImages,
  postDisplayImage,
  delDisplayImage,
  getDisplayData,
  postDisplayData,
  delDisplayData,
  getDisplayText,
  postDisplayText,
  delDisplayText,
} from '../api/cad.js';
import { updatePhysicalPoint } from '../api/front.js';
import * as echarts from 'echarts';
import VueDraggableResizable from 'vue-draggable-resizable';
import 'vue-draggable-resizable/dist/VueDraggableResizable.css';

const toolbarArray = [
  [
    {
      text: '运维模式',
      img: require('../assets/menu/yunwei.png'),
      model: 'operation',
    },
  ],
  [
    {
      text: '饼状图',
      img: require('../assets/menu/en-bz.png'),
      model: 'energy',
      des: 'update',
    },
    {
      text: '柱状图',
      img: require('../assets/menu/en-zz.png'),
      model: 'energy',
      des: 'delete',
    },
    {
      text: '曲线图',
      img: require('../assets/menu/en-qx.png'),
      model: 'energy',
      des: 'delete',
    },
    {
      text: '数据',
      img: require('../assets/menu/en-data.png'),
      model: 'energy',
      des: 'delete',
    },
    {
      text: '文本',
      img: require('../assets/menu/en-wz.png'),
      model: 'energy',
      des: 'update',
    },
    {
      text: '图片',
      img: require('../assets/menu/picture.png'),
      model: 'energy',
      des: 'update',
    },
    {
      text: '时间表',
      img: require('../assets/menu/time.png'),
      model: 'energy',
      des: 'update',
    },
    {
      text: '页面链接',
      img: require('../assets/link.png'),
      model: 'energy',
      des: 'update',
    },
    {
      text: '编辑模式',
      img: require('../assets/menu/edit_icon.png'),
      model: 'editmode',
    },
  ],
];
const propertybarArray = [
  { text: '项目', img: require('../assets/menu/xiangmutubiao.png') },
  { text: '设备管理', img: require('../assets/menu/chilun.png') },
  { text: '能源管理', img: require('../assets/menu/nengyuan.png') },
  { text: '资产管理', img: require('../assets/menu/zichan.png') },
  { text: '人员管理', img: require('../assets/menu/people.png') },
];
const dataTableArray = [
  ['名称', '总数', '运行', '异常'],
  ['风机盘管', '218', '6', '18'],
  ['新风机组', '10', '0', '0'],
  ['照明回路', '174', '0', '0'],
  ['照明配电柜', '7', '7', '0'],
  ['排风机', '1', '0', '0'],
  ['楼顶配电柜', '5', '5', '0'],
];
const groupContextMenu = [
  {
    context: '添加逻辑组',
    sign: 'group',
    img: require('../assets/group/luoji.png'),
  },
  {
    context: '添加3D页面',
    sign: 'threeD',
    img: require('../assets/group/3D.png'),
  },
  {
    context: '添加CAD页面',
    sign: 'cad',
    img: require('../assets/group/cad.png'),
  },
  {
    context: '添加图片页面',
    sign: 'image',
    img: require('../assets/group/pictrue.png'),
  },
  {
    context: '添加链接',
    sign: 'link',
    img: require('../assets/group/lianjie.png'),
  },
  { context: '编辑', sign: 'edit', img: require('../assets/group/edit.png') },
  {
    context: '删除',
    sign: 'delete',
    img: require('../assets/group/delete.png'),
  },
];
export default {
  name: 'Visual',
  components: {
    vue3dLoader,
    FulfillingBouncingCircleSpinner,
    VueDraggableResizable,
    Tinymce,
    FanDialog,
    FloatWindow,
    AngleView,
    ThreeDdialog,
    AddButton,
    AddImage,
    TimeChart,
    TopDialog,
    LeftDialog,
    showModel,
  },
  data() {
    return {
      hasAssetThree: false,
      assetMenuText: '添加资产',
      assetMenuIcon: require('@/assets/menu/tianjiazichan.png'),
      tableData: [], // w
      LookDong: false, // w
      LinkDong: false, // wjh
      bianhao: '', // 输入框绑定的链接 wjh
      linkIndex: null, // 当前正在编辑的下标 wjh
      assetList: [
        { name: '', quantity: '', type: '', link: '' }, // 初始一行
      ], // wjh
      threeList: [
        { name: '', quantity: '', type: '', link: '', modelName: '' }, // 初始一行
      ], // w
      linkthreeIndex: null, // w
      threehao: '', // w
      ThreeDong: false, // w
      LookthhreeDong: false, // w
      tablehreeData: [], // w
      autoChartsRefreshTimer: null, //用于记录charts看板定时器ID
      charts: [],
      showCharts: false,
      texts: [],
      chartCounter: 0,
      textCounter: 0,
      chartInstances: {},
      textContent: '',
      textShowSize: '',
      textColor: '#000000',
      title: '创建饼状图',
      energyModel: false,
      energyModelPie: false,
      energyModelLine: false,
      showTextDialog: false,
      backgroundImage: '', // 存储背景图片
      uploadDialogVisible: false, // 控制上传对话框的显示
      currentDisplaySign: '',
      currentDisplayEditId: void 0,
      // 区分当前点击的是饼状图，柱状图，曲线图
      currentShowDisplay: '', // pie bar line
      customPieFloor: '',
      customPiePostFloor: '',
      customPiePostWidth: 200,
      customPiePostHeight: 200,
      customPiePostX: 0,
      customPiePostY: 0,
      customPiePostBackgroundColor: '',
      customPiePostNameBackgroundColor: '#ccc',

      customBarFloor: '',
      customBarPostFloor: '',
      customBarPostWidth: 200,
      customBarPostHeight: 200,
      customBarPostX: 0,
      customBarPostY: 0,
      customBarPostBackgroundColor: '',
      customBarPostNameBackgroundColor: '#ccc',

      customLineFloor: '',
      customLinePostFloor: '',
      customLinePostWidth: 200,
      customLinePostHeight: 200,
      customLinePostX: 0,
      customLinePostY: 0,
      customLinePostBackgroundColor: '',
      customLinePostNameBackgroundColor: '#ccc',

      radio: 'log',
      dataTable: [
        {
          name: '能耗数据1',
          color: '#409EFF',
        },
        {
          name: '能耗数据2',
          color: '#409EFF',
        },
      ],
      dataColor: '#409EFF',
      pieChatAddFrontData: [],
      pieChartData: [],
      histogramAddFrontData: {}, // 看板折线图添加的数据
      yAxisMaxBar: 100,
      yAxisMinBar: 0,
      yAxisUnitBar: '%',
      barChartHours: void 0,
      barChartData: [],
      yAxisMaxLine: 100,
      yAxisMinLine: 0,
      yAxisUnitLine: '%',
      lineChatAddFrontData: [],

      lineChartData: [],
      lineChartHours: void 0,

      // 全局性标识记录
      globalTopDataSign: '', // data text time
      boardDataForm: {
        visiable: false,
        chooseName: void 0,
        dataId: void 0,
        redisKey: void 0,
        mqttId: void 0,
        x: void 0,
        y: void 0,
        designation: '',
        unit: '',
        size: '11',
        color: '#ccc',
      },
      boardTextForm: {
        visiable: false,
        x: void 0,
        y: void 0,
        text: '',
        link: '',
        size: '',
        color: '',
        bold: '',
      },

      // 判断运维模式还是编辑模式
      isFullscreen: true,
      currentViewMode: 'operation', // operation 运维模式 editmode 编辑模式 defalut mode is editmode
      editmodeDeleteIconFlag: false,
      editmodeDeleteIconDataId: void 0,
      weatherForecast: {
        // 天气预报
        weather: '',
        humidity: '',
        temp: '',
      },
      scene: null,
      camera: null,
      renderer: null,
      model: null,
      controls: null, // 控制器
      raycaster: new THREE.Raycaster(), // 射线检测器
      mouse: new THREE.Vector2(), // 鼠标位置
      dialogVisible: false, // 控制 dialog 是否显示
      dialogVisible2: false,
      selectedObjectName: '', // 被点击物体的名称
      clock: null,
      flowMaterial: null,
      targetMesh: null, // 记录特定模块
      deviceImageVisiable: false,

      isTopView: true,
      clickCurrentFloor: '全楼',
      treeDialog: false, // 树状目录
      treeData: [],
      defaultProps: { children: 'children', label: 'name' },
      addCADDataDialog: false,
      labelPosition: 'left',
      addCADDataForm: {
        showEffect: 'numerical', // numerical-数值 picture-图像 textArea-文本
        chooseTreeName: '',
        chooseTreeDataId: void 0,
        dataId: '',
        redisKey: '',
        mqttId: '',
        xAxis: void 0,
        yAxis: void 0,
        numerical: {
          designation: '',
          unit: '',
          fontSize: '11',
          fontColor: '#64c8fc',
        },
        picture: {
          btnShowTips: '',
          status: '',
        },
        textArea: {
          one: { text: '', fontSize: 11, color: '#64c8fc', flicker: false },
          two: { text: '', fontSize: 11, color: '#64c8fc', flicker: false },
        },
      },
      map: void 0,
      isMapLoading: false,
      fullOrfloor: false, // true为全楼,false为cad楼层
      showModelVisiable: false, // 3D模型
      loading: false, // 正在加载中
      eventMap: void 0, // 保存事件对象
      createInstance: require('../assets/menu/chuangjian.png'),
      associatedDataIcon: require('../assets/menu/guanlianshuju.png'),
      dataMenuIcon: require('../assets/visual_images/r_menu/shuju.png'),
      addDataMenuIcon: require('../assets/menu/addshuju.png'),
      videoMenuIcon: require('../assets/visual_images/r_menu/shipin.png'),
      templateMenuIcon: require('../assets/menu/shebeimuban.png'),
      bookMarkMenuIcon: require('../assets/visual_images/r_menu/shuqian.png'),
      panoramicViewIcon: require('../assets/menu/quanjingshitu.png'),
      addAssetsMenuIcon: require('../assets/menu/tianjiazichan.png'),
      // w
      viewAssetsMenuIcon: require('../assets/menu/look.png'),
      editAssetsMenuIcon: require('../assets/group/edit.png'),
      // w
      addPersonIcon: require('../assets/menu/xinzengrenyuan.png'),
      projectUnActiveIcon: require('../assets/menu/xiangmu_unactive.png'),
      peopleActiveIcon: require('../assets/menu/people_active.png'),
      erweimaIcon: require('../assets/erweima.png'),
      toolbarData: { toolbarCurrentModel: toolbarArray[0] },
      currentPropertyIndex: 0,
      propertybarArray, // 引用魔法变量
      dataTableArray,
      CADArray: [],
      rightFloatView: true,
      addCADPanoramaDialog: false,
      uploadCADPanoramaDialog: false,
      uploadCADPanoramaText: '',
      addPanoramaTextData: {
        text: '',
        link: '',
        fontSize: 11,
        fontColor: 'rgba(38, 38, 38, 1)',
      },
      addPanoramaImagePath: '',
      uploadPanoramaDataImageFormData: void 0,
      uploadCADPanoramaFormData: new FormData(),
      showEffectText: '图形',
      viewer: null, // 保存 Viewer 实例
      panoramaImgLocalPath: require('../assets/panorama.jpg'),
      panoramaImgPath: void 0,
      panoramaCurrentID: void 0,
      panoramaName: '全景视图',
      panoramaNameShowMome: '',
      addPanoramaXAxis: void 0,
      addPanoramaYAxis: void 0,
      addBookmarkDialog: false,
      memoText: '',
      addBookmarkPanoramaDialog: false,
      memoPanoramaText: '',
      showAddDataFlag: true,
      addPanoramaTextDialog: false,
      addPanoramaPictrue: false,
      operationText: '',
      operationTextX: void 0,
      operationTextY: void 0,
      operationTextSign: '',
      operationTextId: '',
      addDataModalFlag: true,

      mainToggle: true,
      currentClickProperty: '',
      relevanceDialog: false,
      limitCadClickBehavior: false, // 限制cad点击行为
      assetDialog: false,
      threesDialog: false, // w
      // 新建cad
      saveMapData: {
        saveCADDialog: false,
        saveMapId: '',
        saveMapName: '',
        saveMapDes: '',
      },
      // 修改cad
      updateMapData: {
        updateCADDialog: false,
        updateId: '',
        updateMapId: '',
        updateMapName: '',
        updateMapDescribe: '',
      },
      // 删除cad图层
      deleteMapData: {
        deleteCADDialog: false,
        deleteMapId: '',
      },
      // 空间ID数据
      spaceData: [],
      labelFeatures: [], // labelFeatures 文字数据源
      // 点击的空间ID数据
      currentClickSpaceData: {},
      // 空间id的删除
      deleteSpaceDialog: false,
      // 创建空间ID
      createSpaceData: {
        spaceIDDialog: false,
        spaceIDName: '',
        spaceDescribe: '',
        spaceFontSize: 11,
        spaceFontColor: '#64c8fc',
      },
      // 编辑空间ID
      updateSpaceData: {
        updateSpaceIDDialog: false,
        updateSpaceId: '',
        updateSpaceName: '',
        updateSpaceDescribe: '',
        updateSpaceFontSize: '',
        updateSpaceColor: '',
      },
      // 添加全景视图
      createPanorama: {
        createIconIndex: void 0,
        createIconArray: [
          require('../assets/yunwei/panorama1.png'),
          require('../assets/yunwei/panorama2.png'),
          require('../assets/yunwei/panorama3.png'),
          require('../assets/yunwei/panorama4.png'),
          require('../assets/yunwei/panorama5.png'),
          require('../assets/yunwei/panorama6.png'),
          require('../assets/yunwei/panorama7.png'),
        ],
        createShowVisiable: false,
        createPanoramaName: '',
        xAxis: void 0,
        yAxis: void 0,
      },
      panoramaSign: '',
      panoramaDes: '',
      panoramaUploadDes: '',
      panoramaEditX: '',
      panoramaEditY: '',
      createCameraSign: 'cad', // 区分创建摄像机的标识 cad / panorama
      // 添加摄像机
      createCamera: {
        createIconIndex: void 0,
        createIconArray: [
          require('../assets/yunwei/camera1.png'),
          require('../assets/yunwei/camera2.png'),
          require('../assets/yunwei/camera3.png'),
          require('../assets/yunwei/camera4.png'),
          require('../assets/yunwei/camera5.png'),
          require('../assets/yunwei/camera6.png'),
          require('../assets/yunwei/camera7.png'),
        ],
        createShowVisiable: false,
        createCameraSerial: '',
        createCameraPath: '',
        createCameraToken: '',
        xAxis: void 0,
        yAxis: void 0,
      },
      // 添加设备模板
      createDevice: {
        createIconIndex: void 0,
        createIconArray: [
          require('../assets/yunwei/device1.png'),
          require('../assets/yunwei/device2.png'),
          require('../assets/yunwei/device3.png'),
          require('../assets/yunwei/device4.png'),
          require('../assets/yunwei/device5.png'),
          require('../assets/yunwei/device6.png'),
          require('../assets/yunwei/device7.png'),
        ],
        createShowVisiable: false,
        createDeviceIcu: '',
        createDeviceTemplate: '',
        xAxis: void 0,
        yAxis: void 0,
      },
      createDeviceShowCheck: false,
      // 设备模板图片合集
      createDevicePictureArr: [
        require('../assets/yunwei/temp1.png'),
        require('../assets/yunwei/temp2.png'),
        require('../assets/yunwei/temp3.png'),
      ],
      createDeviceSelectedImageIndex: null, // 默认没有选中
      // 点击的添加数据
      currentClickCADAddData: {},
      QRdata: {
        QRcodeShow: false, // 二维码
        QRcodeImg: '',
      },
      // 选择数据触发的el tree还是关联数据触发的
      triggerType: 'chooseData',
      //'chooseData' 代表选择数据,
      //'relatedData' 代表关联数据
      //'panoramaData' 代表全景图选择的数据
      //'panoramaButton'代表全景图添加按钮选择数据
      createGroupVisiable: false,
      createGroupInputValue: '',
      createGroupPageVisiable: false,
      groupContextMenu,
      visible3dModelControlVisiable: false,
      rightTreeMenuShowRefresh: false,
      menuVisible: false,
      menuX: 0,
      menuY: 0,
      currentTreeNode: null,
      hideMenuTimer: null,
      raycaster: new THREE.Raycaster(), // 射线检测器
      mouse: new THREE.Vector2(), // 鼠标位置
      dialogVisible: false, // 控制 dialog 是否显示
      selectedObjectName: '', // 被点击物体的名称
      three_monitoring: true, //true为全景图  false为摄像头,
      panoramaLongitude: void 0,
      panoramaLatitude: void 0,
      markersPlugin: null, // 需要初始化
      contextMenuInited: false,
      treeLevelCurrentId: void 0, // 自动获取的树目录还是手动点击树目录去更新，都对应此ID
      treeLevelData: [],
      treeLeveSelectedNodeId: null, // 当前选中的节点id
      treeLevelControlVisiable: false,
      treeLevelControlData: [],
      treeLevelControlChooseData: [],
      currentNodeIsIcu: false, // 记录当前点击的弹窗是关于icu
      recordRightTreeNodeId: void 0, // 记录当前点击右侧菜单的 node id 和 map id
      recordRightTreeMapId: void 0,
      treeLeveShowImage: void 0,
      defaultUnfoldTreeLevelDataIndex: void 0,
      treeLevelDialogData: {
        defaultProps: { children: 'child', label: 'name' },
        treeDialogVisiable: false,
        treeMenuText: '',
        treeMenuType: '',
        editPutId: '',
        editPutParentID: '',
        treeMenuGroup: {
          text: '',
        },
        treeMenuThreeD: {
          text: '',
          path: '',
          cameraAxis: {
            x: '',
            y: '',
            z: '',
          },
        },
        treeMenuImage: {
          text: '',
          file: null,
        },
        treeMenuCad: {
          text: '',
          mapID: '',
        },
        treeMenuLink: {
          text: '',
          path: '',
        },
      },
      treeDialogDeleteVisiable: false,
      addPanoramaDataMark: '',
      cadPanoramaShowData: [],
      addPanoramaButtonData: {
        visiable: false,
        chooseFrontDataName: '',
        dataId: '',
        mqttId: '',
        redisKey: '',
        description: '',
        selectIconIndex: void 0,
        iconArray: [
          [require('../assets/menu/switch_close.png'), require('../assets/menu/switch_open.png')],
          [require('../assets/menu/light_close.png'), require('../assets/menu/light_open.png')],
          [require('../assets/menu/air1_close.png'), require('../assets/menu/air1_open.png')],
          [require('../assets/menu/air2_close.png'), require('../assets/menu/air2_open.png')],
          [require('../assets/menu/switch2_close.png'), require('../assets/menu/switch2_open.png')],
        ],
      },
      showAngleView: false,
      // 新增加的属性
      nodeData: {},
      threeModelData: {},
      light: '', // 光源
      directionalLight: '', // 平行光
      currentModel: null,
      animationId: null,
      onControlsChange: null, // 保存监听函数引用
      selectedModelName: '',
      models: [],
      threeDAdd: false,
      modelName: '',
      showMenu: false,
      menuStyle: { position: 'absolute', top: '0px', left: '0px', zIndex: 10 },
      selectedObject: null,
      hoveredName: '',
      labelStyle: {
        position: 'absolute',
        color: '#fff',
        background: 'rgba(0,0,0,0.7)',
        padding: '4px 8px',
        borderRadius: '4px',
        pointerEvents: 'none',
        transform: 'translate(-50%, -100%)',
        display: 'none',
      },
      threeDDataBindingShow: false,
      threeDFormData: {
        low: { min: 0, max: 30, color: '#FF0000' },
        mid: { min: 31, max: 70, color: '#00FF00' },
        high: { min: 71, max: 100, color: '#0000FF' },
      },
      threeDYemian: false,
      threeDDeviceName: '', //3D模型创建模板的名称
      threeDPanoramaName: '', //3D模型创建全景图的名称
      formattedModels: [],
      panoramaArrayThree: [], // zyp 全景图获取后存储的数据
      deviceArrayThree: [], // zyp 设备获取后存储的数据
      timer: null, // 每秒去刷新数据
      threeDIcuData: {}, // zyp3 设备模板ID
      selectDeviceDataOk: [], // zyp3 承接查询到的设备模板,
      threeDmenu: {
        delete1: false,
        delete2: false,
        delete3: false,
        delete4: false,
        canClick4: true, // 设备模板
        canClick41: true,
        canClick3: true, // 全景图
        canClick31: true,
        canClick2: true, // 模型
        canClick1: false,
      },
      showAddButtonDialog: false,
      // zyp5
      buttons: [],
      dragTarget: null,
      offsetX: 0,
      offsetY: 0,
      showBtnBox: true,
      timeCharts: [], // 所有已选时间表
      dragging: false,
      dragIndex: null,
      displayImageArray: [],
      leftDialogShowContent: void 0,
      leftDialogTargetShowTree: false,
      leftDialogTargetShowDetail: false,

      displayTextArray: [],
      displayTextStatus: '',
      displayTextEditId: void 0,
      // 切割
      topPlane: null,
      cutValue: 25, // 初始高度

      threePanoramaFile: null,
      threePanoramaCadId: '',
      // new project data
      showDeviceData: [
        {
          label: '国家科技传播中心',
          extra: '3884',
          children: [
            {
              label: '设备健康状况',
              extra: '982',
              children: [
                {
                  label: '照明设备',
                  extra: '500',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
                {
                  label: '空调设备',
                  extra: '300',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
                {
                  label: '盘管设备',
                  extra: '182',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
              ],
            },
            {
              label: '系统健康状况',
              extra: '1592',
              children: [
                {
                  label: '供电系统',
                  extra: '500',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
                {
                  label: '空调系统',
                  extra: '500',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
                {
                  label: '弱电系统',
                  extra: '592',
                  children: [
                    {
                      label: '4M层',
                      extra: '50',
                    },
                    {
                      label: '4层',
                      extra: '50',
                    },
                    {
                      label: '3M层',
                      extra: '50',
                    },
                    {
                      label: '3层',
                      extra: '50',
                    },
                    {
                      label: '2M层',
                      extra: '50',
                    },
                    {
                      label: '2层',
                      extra: '50',
                    },
                    {
                      label: '1M层',
                      extra: '50',
                    },
                    {
                      label: '1层',
                      extra: '50',
                    },
                    {
                      label: 'B1层',
                      extra: '50',
                    },
                    {
                      label: 'B2层',
                      extra: '50',
                    },
                  ],
                },
              ],
            },
            {
              label: '环境健康状况',
              extra: '2000',
              children: [
                {
                  label: '4M层',
                  extra: '50',
                },
                {
                  label: '4层',
                  extra: '50',
                },
                {
                  label: '3M层',
                  extra: '50',
                },
                {
                  label: '3层',
                  extra: '50',
                },
                {
                  label: '2M层',
                  extra: '50',
                },
                {
                  label: '2层',
                  extra: '50',
                },
                {
                  label: '1M层',
                  extra: '50',
                },
                {
                  label: '1层',
                  extra: '50',
                },
                {
                  label: 'B1层',
                  extra: '50',
                },
                {
                  label: 'B2层',
                  extra: '50',
                },
              ],
            },
          ],
        },
      ],
      testDefaultProps: {
        children: 'children',
        label: 'label',
      },
      alarmModuleVisiable: false,
      alarmTableData: [
        {
          status: '报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '确认报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '未发送',
          operationUser: 'system',
        },
        {
          status: '报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '未发送',
          operationUser: 'system',
        },
        {
          status: '确认报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '确认报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
        {
          status: '确认报警',
          limit: '50',
          lasttime: '2025-08-10 7:55:32',
          alarmObject:
            '物联网基础数据管理平台-国家科技传播中心-龙芯服务器-空调/新风机组-F3J-KH-B-F3J-5 演播厅 3m04-ACUCMD-BW_ON',
          alarmDes: '定时修改BW_ON的值失败',
          triggerTime: '2024-11-26 6:3:13',
          SendWork: '已发送',
          operationUser: 'system',
        },
      ],
      
    };
  },
  async created() {
    setRem(16);
  },
  mounted() {
    console.log(this.$route.meta, 'route');

    this.getWeather();
    this.getRightTree();

    document.body.addEventListener('mouseover', e => {
      // text-control-marker
      let marker = e.target.closest('.text-control-marker');
      let closeBtn = marker?.querySelector('.text-marker-close');
      if (marker && closeBtn && this.currentViewMode === 'editmode') {
        closeBtn.style.display = 'block';
        return;
      }

      marker = e.target.closest('.button-control-marker');
      closeBtn = marker?.querySelector('.img-marker-close');
      if (marker && closeBtn && this.currentViewMode === 'editmode') {
        closeBtn.style.display = 'block';
        return;
      }

      let marker1 = e.target.closest('.video-control-marker');
      let closeBtn1 = marker?.querySelector('.camera-marker-close');
      if (marker1 && closeBtn1 && this.currentViewMode === 'editmode') {
        closeBtn1.style.display = 'block';
        return;
      }

      // button-control-marker
      marker = e.target.closest('.img-marker');
      closeBtn = marker?.querySelector('.img-marker-close');
      if (marker && closeBtn && this.currentViewMode === 'editmode') {
        closeBtn.style.display = 'block';
        return;
      }

      // text-marker
      marker = e.target.closest('.text-marker');
      closeBtn = marker?.querySelector('.text-marker-close');
      if (marker && closeBtn && this.currentViewMode === 'editmode') {
        closeBtn.style.display = 'block';
        return;
      }
    });

    // ========== 鼠标移出 ==========
    document.body.addEventListener('mouseout', e => {
      // text-control-marker
      let marker = e.target.closest('.text-control-marker');
      let closeBtn = marker?.querySelector('.text-marker-close');
      if (marker && closeBtn) {
        closeBtn.style.display = 'none';
        return;
      }

      // button-control-marker
      marker = e.target.closest('.button-control-marker');
      closeBtn = marker?.querySelector('.img-marker-close');
      if (marker && closeBtn) {
        closeBtn.style.display = 'none';
        return;
      }

      marker = e.target.closest('.img-marker');
      closeBtn = marker?.querySelector('.img-marker-close');
      if (marker && closeBtn) {
        closeBtn.style.display = 'none';
        return;
      }

      // text-marker
      marker = e.target.closest('.text-marker');
      closeBtn = marker?.querySelector('.text-marker-close');
      if (marker && closeBtn) {
        closeBtn.style.display = 'none';
        return;
      }

      let marker1 = e.target.closest('.video-control-marker');
      let closeBtn1 = marker?.querySelector('.camera-marker-close');
      if (marker1 && closeBtn1) {
        closeBtn1.style.display = 'none';
        return;
      }
    });

    // 监听全景图删除数据
    document.body.addEventListener('click', e => {
      const closeBtn = e.target.closest('.text-marker-close, .img-marker-close');
      const controlBtn = e.target.closest('.button-control-marker');
      const cameraBtn = e.target.closest('.video-control-marker');
      const cameraCloseBtn = e.target.closest('.camera-marker-close');
      if (closeBtn) {
        const id = closeBtn.getAttribute('data-id');
        // 调用删除接口
        deletePanoramaData(id).then(res => {
          if (res.success) {
            console.log(`删除成功,ID${id}`);
            this.$message.success('数据删除成功');
            const type = closeBtn.classList.contains('img-marker-close') ? 'img' : 'text';
            const markerId = `${type}_${id}`;
            this.markersPlugin.removeMarker(markerId);
          } else {
            console.error('删除失败：', res.message);
          }
        });
        return;
      }
      if (cameraCloseBtn) {
        const id = cameraCloseBtn.getAttribute('data-id');
        // 调用删除接口
        deletePanoramaVideo(id).then(res => {
          console.log(res);
          if (res.success) {
            this.$message.success('数据删除成功');
            const markerId = `video_${id}`;
            this.markersPlugin.removeMarker(markerId);
          } else {
            console.error('删除失败：', res.message);
          }
        });
        return;
      }
      // 判断是否点击的是 marker 本身（文字）
      const marker = e.target.closest('.text-marker, .img-marker');
      if (this.currentViewMode == 'editmode') {
        const plainTextMarker = e.target.closest('.plain-text');
        if (plainTextMarker) {
          const id = plainTextMarker.getAttribute('data-id');
          if (this.currentViewMode == 'editmode') {
            getPanoramaDataBySpaceId({
              panoramaId: this.panoramaCurrentID,
              type: 'text',
            }).then(res => {
              if (res.success && Array.isArray(res.data)) {
                const item = res.data.find(i => String(i.id) === String(id));
                if (item?.makerModel) {
                  try {
                    const model = item.makerModel;
                    this.operationText = '编辑';
                    this.operationTextId = id;
                    this.operationTextSign = 'edit';
                    this.operationTextX = item.x;
                    this.operationTextY = item.y;
                    this.addPanoramaTextData.text = model.text;
                    this.addPanoramaTextData.link = model.link;
                    this.addPanoramaTextData.fontSize = parseInt(model.size);
                    this.addPanoramaTextData.fontColor = model.color;
                    this.addPanoramaTextDialog = true;
                  } catch (e) {
                    console.error('解析失败:', e);
                  }
                }
              }
            });
          }
        }
      } else if (this.currentViewMode == 'operation') {
        if (marker) {
          const path = marker.getAttribute('data-path');
          if (path) {
            ipcRenderer.send('open-url', path);
          }
        }
      }
      // 纯文本控制逻辑
      if (controlBtn) {
        const dataId = controlBtn.getAttribute('data-control-id');
        const currentStatus = parseInt(controlBtn.getAttribute('data-control-status'));
        console.log(currentStatus);
        updatePhysicalPoint({
          id: dataId,
          value: currentStatus ? 0 : 1,
        }).then(res => {
          console.log(res, '控制成功');
          this.$message.success('控制成功');
        });
      }
      if (cameraBtn) {
        const playUrl = cameraBtn.getAttribute('data-play-url');
        console.log(playUrl);
        this.createCamera.createCameraPath = playUrl;
        this.dialogVisible = true;
      }
    });

    ipcRenderer.send('store-get', 'token');
    const value = ipcRenderer.sendSync('store-get', 'token');
    console.log('同步读取 token:', value);
    window.addEventListener('keydown', this.handleKeydown);
  },
  beforeDestroy() {
    const viewerContainer = document.querySelector('#viewer');
    if (viewerContainer) {
      viewerContainer.removeEventListener('contextmenu', this.handleContextMenu);
    }
    window.removeEventListener('keydown', this.handleKeydown);
  },
  // w
  computed: {
    showNavbar() {
      return !this.$route.meta.hideLayout;
    },
  },

  methods: {
    addAssetRow() {
      this.assetList.push({
        name: '',
        type: '',
        quantity: '',
        link: '',
      });
    },
    handleKeydown(e) {
      // 检查是否按下 Ctrl + J
      if (e.ctrlKey && e.key.toLowerCase() === 'j') {
        this.toggleFullscreen();
        this.handleFullscreenChange();
      }
    },
    toggleFullscreen() {
      if (screenfull.isEnabled) {
        screenfull.toggle(this.$refs.fullscreenBox);
      }
    },
    handleFullscreenChange() {
      !screenfull.isFullscreen ? (this.isFullscreen = false) : (this.isFullscreen = true);
      !screenfull.isFullscreen ? (this.rightFloatView = true) : (this.rightFloatView = false);
    },
    visualTouch() {
      this.createGroupVisiable = false;
    },
    async getWeather() {
      const API_KEY = 'cb8885d03abc4412ab83212e87a4fddf';
      const location = '101010300';
      const url = `https://devapi.qweather.com/v7/weather/now?location=${location}&key=${API_KEY}`;
      try {
        const response = await axios.get(url);
        if (response.data && response.data.now) {
          this.weatherForecast.weather = response.data.now.text;
          this.weatherForecast.humidity = response.data.now.humidity;
          this.weatherForecast.temp = response.data.now.temp;
        }
      } catch (error) {
        console.error('获取天气数据失败:', error);
      }
    },
    loadCadModel(path) {
      // 从本地获取模型的数据
      const name = this.nodeData.name + '-' + this.nodeData.id;
      ipcRenderer.send('store-get', name);
      ipcRenderer.on('store-get-reply', (event, { key, value }) => {
        console.log(`收到的 ${key}:`, value);
        this.threeModelData = value;
      });
      setTimeout(() => {
        this.reloadSceneWithModel(path);
      }, 500);
      /*  if (!this.renderer) {
         this.init(path); // 第一次初始化
       } else {
         this.clearModel();        // 清除旧模型
         this.loadModel(path);     // 加载新模型
       } */
    },
    async init(path) {
      this.clearModel();
      const staticPath = await ipcRenderer.invoke('get-static-path');
      const container = document.getElementById('three-container');

      if (!this.renderer) {
        // 初始化场景、相机、渲染器
        this.scene = new THREE.Scene();
        // 创建相机
        this.camera = new THREE.PerspectiveCamera(
          75,
          window.innerWidth / window.innerHeight,
          0.1,
          2000
        );
        // 创建渲染器
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setClearColor(0x000c29);
        container.appendChild(this.renderer.domElement);

        // 初始化控制器
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enableZoom = true;
        // this.controls.minDistance = 300;   // 相机最近到5单位
        // this.controls.maxDistance = 400;  // 相机最远到20单位
        this.controls.dampingFactor = 0.25; // 阻尼系数
        this.controls.enableDamping = true; // 开启惯性
        this.controls.screenSpacePanning = true; // 禁止屏幕空间平移
        this.controls.minAzimuthAngle = -Infinity;
        this.controls.maxAzimuthAngle = Infinity;
        this.controls.enableRotate = true; // 默认是 true，可以不用写
        this.controls.enablePan = true; // 禁止拖动
        this.controls.update();

        // 流动贴图材质
        // const flowTexture = new THREE.TextureLoader().load('th.jpg');
        const flowTexture = new THREE.TextureLoader().load(`${staticPath}th.jpg`);
        flowTexture.wrapS = flowTexture.wrapT = THREE.RepeatWrapping;

        this.flowMaterial = new THREE.MeshBasicMaterial({
          map: flowTexture,
          color: new THREE.Color(0x00aaff),
          transparent: true,
          opacity: 0.8,
          side: THREE.DoubleSide,
          depthWrite: false,
        });
        this.loadModel(path);
        this.animate();
      }
    },
    async loadModel(path) {
      const loader = new GLTFLoader();
      this.controls.enableDamping = false;
      // 设置 camera 和 target 前，先重置 target 为原点或当前位置
      this.controls.target.set(0, 0, 0);
      this.camera.position.set(0, 0, 0);
      // 强制更新控制器
      this.controls.update();
      const dom = this.renderer.domElement;
      if (this._onMouseDown) {
        dom.removeEventListener('mousedown', this._onMouseDown);
        dom.removeEventListener('mousemove', this._onMouseMove);
        dom.removeEventListener('click', this._onClickWrapper);
      }
      if (path == 'f4' || path == 'sh1') {
        this.renderer.domElement.removeEventListener('click', this.onMouseClick, false);
        this.controls.minPolarAngle = Math.PI / 2.3; // 最小仰角60°
        this.controls.maxPolarAngle = Math.PI / 2.3; // 最大仰角90°
        this.camera.position.set(271.931, 42.836, -229.053);
        this.controls.target.set(45.287, -18.723, -38.293);

        this.clearLights();
        const ambientLight = new THREE.AmbientLight(0x0073ff, 2.6);
        this.scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0x0073ff, 2.8);
        directionalLight.position.set(10, 20, 10);
        this.scene.add(directionalLight);
      } else {
        // 添加防止拖拽触发点击的逻辑
        this._isDragging = false;
        this._downTime = 0;

        this._onMouseDown = () => {
          this._isDragging = false;
          this._downTime = Date.now();
        };
        this._onMouseMove = () => {
          if (Date.now() - this._downTime > 100) {
            this._isDragging = true;
          }
        };
        this._onClickWrapper = e => {
          if (!this._isDragging) {
            this.onMouseClick(e);
          }
        };

        dom.addEventListener('mousedown', this._onMouseDown);
        dom.addEventListener('mousemove', this._onMouseMove);
        dom.addEventListener('click', this._onClickWrapper);
        this.clearLights();
        // this.controls.minPolarAngle = THREE.MathUtils.degToRad(0);   // 最小仰角：20°
        // this.controls.maxPolarAngle = THREE.MathUtils.degToRad(70);  // 最大仰角：150°
        this.controls.minPolarAngle = Math.PI / 4.3; // 最小仰角60°
        this.controls.maxPolarAngle = Math.PI / 4.3; // 最大仰角90°
        this.camera.position.set(7.18, 137.832, 118.122);
        this.controls.target.set(6.643, -45.232, -24.613);

        const ambientLight = new THREE.AmbientLight(0xffffff, 2.6);
        this.scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0xffffff, 2.8);
        directionalLight.position.set(10, 20, 10);
        this.scene.add(directionalLight);
      }
      // 再次更新控制器，确保立即生效
      this.controls.update();
      // 恢复 damping
      this.controls.enableDamping = true;
      const staticPath = await ipcRenderer.invoke('get-static-path');
      // const modelPath = `${path}.glb`;
      const modelPath = `${staticPath}${path}.glb`;
      console.log(modelPath);

      loader.load(
        modelPath,
        gltf => {
          this.model = gltf.scene;
          this.scene.add(this.model);
          this.model.traverse(child => {
            if (child.isMesh) {
              child.name = child.name || `Object_${child.uuid}`;
              if (child.name == '柱体') {
                console.log('判断循环', child);
                this.targetMesh = child;
                const originalMaterial = child.material.clone();
                child.material = this.flowMaterial;
                child.userData.originalMaterial = originalMaterial;
              }
            }
          });
        },
        xhr => {
          if (((xhr.loaded / xhr.total) * 100).toFixed(2) === '100.00') {
            console.log('模型加载完成');
          }
        },
        error => {
          console.error('模型加载失败:', error);
        }
      );
    },
    // 新的加载模型方法
    reloadSceneWithModel(fileName) {
      clearInterval(this.timer); // 清除定时器 zyp1
      this.cleanupScene(); // 先销毁上一次的渲染环境
      this.initScene();
      this.loadModel(fileName);
      window.addEventListener('resize', this.onWindowResize);
      this.$refs.canvasContainer.addEventListener('mousemove', this.onMouseMove);
      this.$refs.canvasContainer.addEventListener('contextmenu', this.onRightClick);
      document.addEventListener('click', this.onDocumentClick); // ← 加这一行
      this.timer = setInterval(() => {
        this.simulateBackendUpdates();
      }, 1000);
    },
    // 先去清除环境
    cleanupScene() {
      if (this.controls && this.onControlsChange) {
        this.controls.removeEventListener('change', this.onControlsChange);
        this.onControlsChange = null;
      }
      cancelAnimationFrame(this.animationId);
      if (this.controls) {
        this.controls.dispose();
        this.controls = null;
      }
      if (this.scene) {
        this.scene.traverse(obj => {
          if (obj.geometry) obj.geometry.dispose();
          if (obj.material) {
            if (Array.isArray(obj.material)) {
              obj.material.forEach(m => m.dispose());
            } else {
              obj.material.dispose();
            }
          }
        });
        this.scene = null;
      }
      if (this.renderer) {
        this.renderer.dispose();
        // 移除旧 canvas
        const container = this.$refs.canvasContainer;
        while (container.firstChild) {
          container.removeChild(container.firstChild);
        }
        this.renderer = null;
      }
      this.camera = null;
      this.currentModel = null;
    },
    // 重新init
    initScene() {
      console.log('已经初始化了');
      this.loader = new GLTFLoader();
      const container = this.$refs.canvasContainer;
      // 初始化场景、相机、渲染器
      this.scene = new THREE.Scene();
      // 加载背景图
      // const loader2 = new THREE.TextureLoader();
      // loader2.load('main.png', texture => {
      //   this.scene.background = texture;
      // });
      const loader2 = new THREE.TextureLoader();
      loader2.load('main.png', texture => {
        texture.colorSpace = THREE.SRGBColorSpace; // three.js r152+
        this.scene.background = texture;
      });
      // 创建相机
      this.camera = new THREE.PerspectiveCamera(
        750,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      // 创建渲染器
      this.renderer = new THREE.WebGLRenderer();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.renderer.setClearColor(0x000c29);
      this.renderer.localClippingEnabled = true;
      container.appendChild(this.renderer.domElement);
      // 添加光源0073ff
      if (this.threeModelData) {
        console.log('this.threeModelData', this.threeModelData);
      }
      this.light = new THREE.AmbientLight(0xffffff, 2.6); // Ambient light
      this.scene.add(this.light);
      this.directionalLight = new THREE.DirectionalLight(0xffffff, 2.8);
      this.directionalLight.position.set(10, 20, 10);
      this.scene.add(this.directionalLight);
      // 初始化控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableZoom = true;
      this.controls.minDistance = 30; // 相机最近到5单位
      this.controls.maxDistance = 400; // 相机最远到20单位
      this.controls.dampingFactor = 0.25; // 阻尼系数
      this.controls.enableDamping = true; // 开启惯性
      this.controls.screenSpacePanning = true; // 禁止屏幕空间平移
      this.controls.minAzimuthAngle = -Infinity;
      this.controls.maxAzimuthAngle = Infinity;
      this.controls.enableRotate = true; // 默认是 true，可以不用写
      this.controls.enablePan = true; // 禁止拖动
      this.controls.update();
      this.updateCamera(this.threeModelData);
      // 这个是用来监听窗口大小变化，更新渲染器大小
      if (this.showAngleView && this.currentViewMode == 'editmode') {
        this.$nextTick(() => {
          this.onControlsChange = () => {
            this.$refs.cameraPanel.updateCameraAndTarget({
              pos: this.camera.position,
              target: this.controls.target,
            });
          };
          this.controls.addEventListener('change', this.onControlsChange);
        });
      }
      // 用来绑定点击事件
      // if (this.currentViewMode === 'editmode') {
      const dom = this.renderer.domElement;
      this.raycaster = new THREE.Raycaster();
      this.mouse = new THREE.Vector2();
      this._isDragging = false;
      this._downTime = 0;

      this._onMouseDown = () => {
        this._isDragging = false;
        this._downTime = Date.now();
      };
      this._onMouseMove = () => {
        if (Date.now() - this._downTime > 100) {
          this._isDragging = true;
        }
      };
      this._onClickWrapper = e => {
        if (!this._isDragging) {
          this.onModelClick(e);
        }
      };
      dom.addEventListener('mousedown', this._onMouseDown);
      dom.addEventListener('mousemove', this._onMouseMove);
      dom.addEventListener('click', this._onClickWrapper);
      // }
      // 切割平面
      this.topPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), this.cutValue);
      this.animate();
    },
    isUnderNamed(node, targetName) {
      let p = node;
      while (p) {
        if (p.name === targetName) return true;
        p = p.parent;
      }
      return false;
    },

    cloneMatWithClipping(mat) {
      if (!mat) return mat;
      if (Array.isArray(mat)) {
        return mat.map(m => {
          const nm = m.clone ? m.clone() : m;
          nm.clippingPlanes = [this.topPlane];
          nm.clipShadows = true;
          return nm;
        });
      } else {
        const nm = mat.clone ? mat.clone() : mat;
        nm.clippingPlanes = [this.topPlane];
        nm.clipShadows = true;
        return nm;
      }
    },
    async loadModel(fileName) {
      const staticPath = await ipcRenderer.invoke('get-static-path');
      // const modelPath = `${fileName}.glb`;
      const modelPath = `${staticPath}${fileName}.glb`;
      this.loader.load(
        modelPath,
        gltf => {
          let clippedCount = 0;
          this.currentModel = gltf.scene;
          gltf.scene.traverse(child => {
            if (child.isMesh) {
              this.models.push(child);
              child.name = child.name || '未命名模型';
              // const isColumn =
              //   child.name === '柱体' ||
              //   child.name.startsWith('柱体') ||
              //   this.isUnderNamed(child, '柱体');

              // const isMain = this.isUnderNamed(child, '国传主体模型');

              // if (isColumn || isMain) {
              child.material = this.cloneMatWithClipping(child.material);
              clippedCount++;
              // }
            }
          });
          this.scene.add(gltf.scene);
        },
        undefined,
        error => {
          console.error('加载失败:', error);
        }
      );
    },
    updateCutPlane() {
      this.topPlane.constant = this.cutValue;
    },

    setCut(val) {
      this.cutValue = val;
      this.updateCutPlane();
    },

    animate() {
      this.animationId = requestAnimationFrame(this.animate);
      if (this.controls) this.controls.update();
      if (this.renderer && this.scene && this.camera) {
        this.renderer.render(this.scene, this.camera);
      }
    },
    // 3D模型打开全景图
    threeDModelOpenPanorama(modlename) {
      // zyp
      this.threeDYemian = true;
      this.modelName = modlename;
      // modelename 是全景图的名字
      this.threeDAdd = true;
      console.log('nodeData', this.nodeData);
      this.createPanorama.createShowVisiable = true;
    },
    // 打开3D模型设备模板
    threeDModelDevice(modlename) {
      this.threeDYemian = true;
      this.modelName = modlename;
      this.threeDAdd = true;
      console.log('nodeData', this.nodeData);
      this.createDevice.createShowVisiable = true;
    },
    // 选择3D树状目录的数据
    selectThreeDTreeData(modlename) {
      this.threeDYemian = true;
      this.modelName = modlename;
      this.threeDAdd = true;
      console.log('nodeData', this.nodeData);
      this.treeDialog = true;
      this.getDataTree();
    },
    // 点击的方法
    onModelClick(event) {
      console.log('点击了模型', event);
      this.handleLeftDialogShowDetail();
      // 1. 弹窗立即打开
      this.selectedModelName = '加载中...';

      // 2. 使用 setTimeout 推迟模型点击检测，避免阻塞 UI
      setTimeout(() => {
        const rect = this.renderer.domElement.getBoundingClientRect();
        this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

        this.raycaster.setFromCamera(this.mouse, this.camera);
        const intersects = this.raycaster.intersectObjects(this.scene.children, true);

        if (intersects.length > 0) {
          // 遍历，找第一个在 cut 平面以下的命中对象
          let validHit = null;
          for (let i = 0; i < intersects.length; i++) {
            if (intersects[i].point.y <= this.cutValue) {
              validHit = intersects[i];
              break;
            }
          }

          if (validHit) {
            const clickedObject = validHit.object;
            const name = clickedObject.name || '未命名模型';
            console.log('✅ 点击到了模型:', name); // 这里会打印名称
            this.selectedModelName = name;
            // this.$refs.threeDialog.threeModuleName(name)
            // 根据模型的名称判断弹出来的是设备模板还是全景图 zyp3
            const panoramaObj = this.checkModelData(name); // 如果panoramaObj有参数证明为全景视图
            const deviceObj = this.checkModelDataDevce(name);

            if (deviceObj) {
              console.log(deviceObj, '点击设备对象');
              this.$refs.threeDialog.openThreeDialog(deviceObj);
            } else if (panoramaObj) {
              this.panoramaCurrentID = panoramaObj.id;
              this.panoramaImgPath = panoramaObj.url;
              this.addCADPanoramaDialog = true;
              this.loadPanorama(false, 'query');
            }
          } else {
            this.selectedModelName = '未识别到模型';
            this.$refs.threeDialog.threeModuleName('未识别到模型');
          }
        } else {
          this.selectedModelName = '未识别到模型';
          this.$refs.threeDialog.threeModuleName('未识别到模型');
        }
      }, 10);
    },
    // 清除环境光源
    clearLights() {
      const lights = this.scene.children.filter(obj => obj.isLight);
      lights.forEach(light => this.scene.remove(light));
    },
    clearModel() {
      if (this.model) {
        console.log('清除旧模型');
        this.scene.remove(this.model);
        this.disposeModel(this.model);
        this.model = null;
      }
    },
    clearCompleteModel() {
      // 如果模型存在，遍历并清除所有资源
      if (this.model) {
        this.model.traverse(child => {
          if (child.isMesh) {
            // 释放材质
            if (child.material) {
              child.material.dispose();
              if (child.material.map) {
                child.material.map.dispose(); // 释放纹理
              }
            }
            // 释放几何体
            if (child.geometry) {
              child.geometry.dispose();
            }
          }
        });
        this.scene.remove(this.model); // 从场景中移除模型
        this.model = null;
      }
      // 释放纹理和材质
      if (this.flowMaterial && this.flowMaterial.map) {
        this.flowMaterial.map.dispose();
        this.flowMaterial.dispose();
      }
      // 移除事件监听器
      this.renderer.domElement.removeEventListener('click', this.onMouseClick, false);
      // 销毁控制器
      if (this.controls) {
        this.controls.dispose(); // 销毁控制器
      }
      // 清除渲染器的内容
      if (this.renderer) {
        this.renderer.dispose(); // 销毁渲染器
        this.renderer = null;
      }
    },
    onMouseClick(event) {
      const rect = this.renderer.domElement.getBoundingClientRect();
      this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
      this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

      this.raycaster.setFromCamera(this.mouse, this.camera);

      if (!this.model) return; // 避免模型未加载时报错
      const intersects = this.raycaster.intersectObject(this.model, true);

      if (intersects.length > 0) {
        const object = intersects[0].object;
        console.log(object.name);
        if (object.name == '平面002') {
          return;
        } else {
          this.selectedObjectName = object.name;
          if (object.name == '69') {
            this.selectedObjectName = '3F07会议室';
            this.dialogVisible = false;
            this.dialogVisible2 = true;
            this.three_monitoring = false;
            this.loadPanorama('model', null);
          } else if (object.name == '68') {
            this.dialogVisible = true;
            this.dialogVisible2 = false;
            this.three_monitoring = true;
            this.selectedObjectName = `${object.name}区域监控`;
          }
          console.log('点击到物体:', object.name);
        }
      }
    },
    disposeModel(model) {
      model.traverse(child => {
        if (child.isMesh) {
          child.geometry.dispose();
          if (child.material.isMaterial) {
            this.disposeMaterial(child.material);
          }
        }
      });
    },
    disposeMaterial(material) {
      if (Array.isArray(material)) {
        material.forEach(m => this.disposeMaterial(m));
      } else {
        material.dispose();
      }
    },
    getImageStyle(index) {
      if (index === 0) {
        return {
          width: '18px',
          height: '18px',
        };
      } else if (index === 4) {
        return {
          width: '25px',
          height: '25px',
        };
      } else {
        return {
          width: '17px',
          height: '17px',
        };
      }
    },
    getToolsImageStyle(index) {
      if (index === 7) {
        return {
          width: '19px',
          height: '19px',
          marginTop: '-2px',
        };
      } else {
        return {
          width: '17px',
          height: '17px',
        };
      }
    },
    getToolsImageTextStyle(index) {
      if (index === 6) {
        return {
          marginTop: '3px',
        };
      }
    },
    async loadPanorama(model = void 0, query = void 0) {
      if (this.viewer) {
        this.clearPanorama();
      }
      if (query) {
        this.panoramaTimer && clearInterval(this.panoramaTimer);
        this.panoramaTimer = setInterval(() => {
          getPanoramaDataBySpaceId({
            panoramaId: this.panoramaCurrentID,
            type: 'data',
          }).then(res => {
            // console.log('查询添加数据', res);
            if (res.success) {
              res.data.forEach(item => {
                const transItem = item.makerModel;
                if (transItem.type == 'number') {
                  const contentText = transItem.name + ':' + transItem.value + transItem.unit;
                  const color = transItem.color;
                  const size = transItem.size;

                  let addMarkers = {
                    id: `text_${item.id}`,
                    longitude: item.x,
                    latitude: item.y,
                    html: `
                    <div class="text-marker" data-id="${item.id}" style="position: relative;">
                      ${contentText}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                    anchor: 'center center',
                    style: {
                      color: color,
                      fontSize: size + 'px',
                      fontFamily: 'Helvetica, sans-serif',
                    },
                    tooltip: '',
                  };
                  this.addOrUpdateMarker(addMarkers);
                  // this.markersPlugin.addMarker(addMarkers);
                  this.$nextTick(() => {
                    const el = document.querySelector(`.text-marker[data-id="${item.id}"]`);
                    const closeBtn = el?.querySelector('.text-marker-close');
                    if (el && closeBtn) {
                      el.addEventListener('mouseenter', () => {
                        if (this.currentViewMode === 'editmode') {
                          closeBtn.style.display = 'block';
                        } else {
                          return;
                        }
                      });
                      el.addEventListener('mouseleave', () => {
                        closeBtn.style.display = 'none';
                      });
                    }
                  });
                } else if (transItem.type == 'word') {
                  const currentSign = parseInt(transItem.value);
                  const contentText = currentSign ? transItem.text1 : transItem.text0;
                  const color = currentSign ? transItem.color1 : transItem.color0;
                  const size = currentSign ? transItem.size1 : transItem.size0;
                  let addMarkers = {
                    id: `text_${item.id}`,
                    longitude: item.x,
                    latitude: item.y,
                    html: `
                    <div class="text-control-marker"
                    data-id="${item.id}" style="position: relative;">
                      ${contentText}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                    anchor: 'center center',
                    style: {
                      color: color,
                      fontSize: size + 'px',
                      fontFamily: 'Helvetica, sans-serif',
                    },
                    tooltip: '',
                  };
                  this.addOrUpdateMarker(addMarkers);
                  // this.markersPlugin.addMarker(addMarkers);
                  this.$nextTick(() => {
                    const el = document.querySelector(`.text-control-marker[data-id="${item.id}"]`);
                    const closeBtn = el?.querySelector('.text-marker-close');
                    if (el && closeBtn) {
                      el.addEventListener('mouseenter', () => {
                        if (this.currentViewMode === 'editmode') {
                          closeBtn.style.display = 'block';
                        } else {
                          return;
                        }
                      });
                      el.addEventListener('mouseleave', () => {
                        closeBtn.style.display = 'none';
                      });
                    }
                  });
                } else if (transItem.type == 'button') {
                  const transBtnItem = item.makerModel;
                  const iconIndex = transBtnItem.buttonType;
                  const iconTips = transBtnItem.name;
                  const iconFlag = parseInt(transBtnItem.value);
                  const iconControlId = transBtnItem.dataId;
                  let addMarkers = {
                    id: `img_${item.id}`,
                    longitude: item.x,
                    latitude: item.y,
                    html: `
                    <div class="button-control-marker" data-control-status="${iconFlag}" data-control-id="${iconControlId}" data-type="button" title="${iconTips}" data-id="${item.id}" style="position: relative;">
                      <img src="${this.addPanoramaButtonData.iconArray[iconIndex][iconFlag]}"
                          style="width: 18px; height: 18px" />
                      <span class="img-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                    anchor: 'center center',
                    style: {},
                    tooltip: '',
                  };
                  this.addOrUpdateMarker(addMarkers);
                  // this.markersPlugin.addMarker(addMarkers);
                  this.$nextTick(() => {
                    const el = document.querySelector(
                      `.button-control-marker[data-id="${item.id}"]`
                    );
                    const closeBtn = el?.querySelector('.img-marker-close');
                    if (el && closeBtn) {
                      el.addEventListener('mouseenter', () => {
                        if (this.currentViewMode === 'editmode') {
                          closeBtn.style.display = 'block';
                        } else {
                          return;
                        }
                      });
                      el.addEventListener('mouseleave', () => {
                        closeBtn.style.display = 'none';
                      });
                    }
                  });
                }
              });
            }
          });
        }, 1000);
        getPanoramaDataBySpaceId({
          panoramaId: this.panoramaCurrentID,
          type: 'graphics',
        }).then(res => {
          console.log('查询添加图片', res);
          if (res.success) {
            res.data.forEach(item => {
              const transItem = item.makerModel;
              let path = transItem.outLink;
              const marker = {
                id: `img_${item.id}`,
                longitude: item.x,
                latitude: item.y,
                html: `
                    <div class="img-marker" data-path="${path}" data-id="${
                  item.id
                }" style="position: relative;">
                      <img src="${transItem.link}"
                          alt="${transItem.fileName || 'marker image'}"
                          style="width:auto; height: auto; border-radius: 6px;" />
                      <span class="img-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                anchor: 'center center',
                style: {},
                tooltip: '',
              };
              this.markersPlugin.addMarker(marker);

              this.$nextTick(() => {
                const el = document.querySelector(`.img-marker[data-id="${item.id}"]`);
                const closeBtn = el?.querySelector('.img-marker-close');
                if (el && closeBtn) {
                  el.addEventListener('mouseenter', () => {
                    if (this.currentViewMode === 'editmode') {
                      closeBtn.style.display = 'block';
                    } else {
                      return;
                    }
                  });
                  el.addEventListener('mouseleave', () => {
                    closeBtn.style.display = 'none';
                  });
                }
              });
            });
          }
        });
        getPanoramaDataBySpaceId({
          panoramaId: this.panoramaCurrentID,
          type: 'text',
        }).then(res => {
          console.log('查询添加文本', res);
          if (res.success) {
            res.data.forEach(item => {
              const transItem = item.makerModel;
              const path = transItem.link;
              const marker = {
                id: `text_${item.id}`,
                longitude: item.x,
                latitude: item.y,
                html: `
                    <div class="text-marker plain-text" data-id="${item.id}" data-path="${path}">
                      ${transItem.text}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                anchor: 'center center',
                style: {
                  color: transItem.color,
                  fontSize: transItem.size,
                  fontFamily: 'Helvetica, sans-serif',
                },
                tooltip: '',
              };
              this.markersPlugin.addMarker(marker);

              this.$nextTick(() => {
                const el = document.querySelector(`.text-marker[data-id="${item.id}"]`);
                const closeBtn = el?.querySelector('.text-marker-close');
                if (el && closeBtn) {
                  el.addEventListener('mouseenter', () => {
                    if (this.currentViewMode === 'editmode') {
                      closeBtn.style.display = 'block';
                    } else {
                      return;
                    }
                  });
                  el.addEventListener('mouseleave', () => {
                    closeBtn.style.display = 'none';
                  });
                }
              });
            });
          }
        });
        getPanoramaAddVideo(this.treeLevelCurrentId, this.panoramaCurrentID).then(res => {
          console.log(res, '查询全景图中的添加摄像机点位数据');
          res.data.forEach(item => {
            console.log(item, 'items');

            const iconIndex = item.iconIndex;
            const iconTips = item.cameraNumber;
            const iconPlayUrl = item.cameraUrl;
            let addMarkers = {
              id: `video_${item.id}`,
              longitude: item.x,
              latitude: item.y,
              html: `
                    <div class="video-control-marker" data-play-url="${iconPlayUrl}" title="${iconTips}" data-id="${item.id}" style="position: relative;">
                      <img src="${this.createCamera.createIconArray[iconIndex]}"
                          style="width: 18px; height: 18px" />
                      <span class="camera-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
              anchor: 'center center',
              style: {},
              tooltip: '',
            };
            this.markersPlugin.addMarker(addMarkers);
            this.$nextTick(() => {
              const el = document.querySelector(`.video-control-marker[data-id="${item.id}"]`);
              const closeBtn = el?.querySelector('.camera-marker-close');
              if (el && closeBtn) {
                el.addEventListener('mouseenter', () => {
                  if (this.currentViewMode === 'editmode') {
                    closeBtn.style.display = 'block';
                  } else {
                    return;
                  }
                });
                el.addEventListener('mouseleave', () => {
                  closeBtn.style.display = 'none';
                });
              }
            });
          });
        });
      }
      this.$nextTick(() => {
        let container = model
          ? document.querySelector('#viewerThree')
          : document.querySelector('#viewer');
        this.viewer = new Viewer({
          container: container,
          panorama: model ? this.panoramaImgLocalPath : this.panoramaImgPath,
          fisheye: false,
          autorotateIdle: true,
          defaultZoomLvl: 50,
          size: {
            width: '100%',
            height: '100%',
          },
          navbar: false,
          plugins: [
            [MarkersPlugin, {}], // 不再传 markers: addMarkers
          ],
        });
        this.markersPlugin = this.viewer.getPlugin(MarkersPlugin);
        this.viewer.on('click', e => {
          console.log('全景图被点击的坐标');
          this.panoramaLongitude = e.args[0].longitude;
          this.panoramaLatitude = e.args[0].latitude;
        });
        this.selfRightMenuInPanorama(model);
      });
    },
    editCadPanorama() {
      getPanoramaByCadId(this.currentTreeNode.id).then(res => {
        console.log(res);
        if (res.success) {
          res.data.forEach(element => {
            if (element.id == this.panoramaCurrentID) {
              this.panoramaEditX = element.x;
              this.panoramaEditY = element.y;
              this.createPanorama.createPanoramaName = element.name;
              this.createPanorama.createIconIndex = element.iconIndex;
              this.createPanorama.createShowVisiable = true;
            }
          });
        }
        console.log(this.panoramaCurrentID, '当前全景图id');
      });
    },
    addOrUpdateMarker(marker) {
      try {
        const existing = this.markersPlugin.getMarker(marker.id);
        if (existing !== undefined) {
          this.markersPlugin.updateMarker(marker);
        } else {
          this.markersPlugin.addMarker(marker);
        }
      } catch (error) {
        console.error('处理标记点时出错:', error);
        this.markersPlugin.addMarker(marker);
      }
    },
    selfRightMenuInPanorama(model) {
      let viewerContainer = model
        ? document.querySelector('#viewerThree')
        : document.querySelector('#viewer');

      viewerContainer.addEventListener('contextmenu', event => {
        if (this.currentViewMode != 'editmode') {
          return; // 非编辑模式下不显示右键菜单
        }
        event.preventDefault(); // 阻止浏览器默认右键菜单

        // 删除已有菜单
        const existingMenu = document.getElementById('custom-context-menu');
        if (existingMenu) {
          existingMenu.remove();
        }

        // 创建菜单容器
        const menu = document.createElement('div');
        menu.id = 'custom-context-menu';
        menu.classList.add('custom-context-menu');
        menu.style.position = 'absolute';
        menu.style.left = `${event.clientX}px`;
        menu.style.top = `${event.clientY}px`;
        menu.style.backgroundColor = 'rgb(37,37,38)';
        menu.style.width = '116px';
        menu.style.padding = '10px';
        menu.style.color = 'rgb(204,204,193)';
        menu.style.fontSize = '12px';
        menu.style.borderRadius = '4px';
        menu.style.boxShadow = '1px 1px 10px #000';
        menu.style.boxSizing = 'border-box';
        menu.style.zIndex = 9999;

        const createMenuItem = (imgSrc, text, onClick) => {
          const item = document.createElement('p');
          item.style.cursor = 'pointer';
          item.style.display = 'flex';
          (item.style.justifyContent = 'flexStart'), (item.style.alignItems = 'center');
          item.style.margin = '6px 0';

          const icon = document.createElement('img');
          icon.src = imgSrc;
          icon.style.width = '16px';
          icon.style.height = '16px';
          icon.style.marginLeft = '12px';
          icon.style.marginRight = '8px';
          const label = document.createElement('span');
          label.textContent = text;

          item.appendChild(icon);
          item.appendChild(label);

          item.addEventListener('click', () => {
            onClick();
            menu.remove();
          });
          return item;
        };

        // 创建各个菜单项
        const addDataMenuItem = createMenuItem(
          require('../assets/menu/addshuju.png'),
          '添加数据',
          () => {
            this.addDataModalFlag = false;
            this.handleCADAddData('panorama');
            this.addPanoramaDataMark = 'markData';
          }
        );

        const addTextMenuItem = createMenuItem(
          require('../assets/menu/en-wz.png'),
          '添加文本',
          () => {
            this.operationText = '添加';
            this.operationTextSign = 'add';
            this.addPanoramaTextDialog = true;
            this.addPanoramaDataMark = 'markText';
          }
        );

        const addPictureMenuItem = createMenuItem(
          require('../assets/group/pictrue.png'),
          '添加图片',
          () => {
            this.addPanoramaPictrue = true;
            this.addPanoramaDataMark = 'markImage';
          }
        );

        const addBtnMenuItem = createMenuItem(
          require('../assets/menu/anniu.png'),
          '添加按钮',
          () => {
            this.addPanoramaButtonData.visiable = true;
            this.addPanoramaDataMark = 'markButton';
            this.triggerType = 'panoramaButton';
          }
        );

        const addTemplateMenuItem = createMenuItem(this.templateMenuIcon, '添加模板', () => {});

        const addVideoMenuItem = createMenuItem(this.videoMenuIcon, '添加视频', () => {
          this.createCameraSign = 'panorama';
          this.createCamera.createCameraPath = '';
          this.createCamera.createShowVisiable = true;
        });

        menu.appendChild(addDataMenuItem);
        menu.appendChild(addTextMenuItem);
        menu.appendChild(addPictureMenuItem);
        menu.appendChild(addBtnMenuItem);
        menu.appendChild(addTemplateMenuItem);
        menu.appendChild(addVideoMenuItem);

        document.body.appendChild(menu);

        // 点击页面其他区域关闭菜单
        this.listenerMenuMethod(menu);
      });
    },
    listenerMenuMethod(menu) {
      const handleDocumentClick = () => {
        if (menu && document.body.contains(menu)) {
          document.body.removeChild(menu);
        }
        document.removeEventListener('click', handleDocumentClick);
      };
      document.addEventListener('click', handleDocumentClick);
    },
    clearPanorama() {
      if (this.viewer) {
        this.viewer.destroy();
        this.viewer = null;
      }
    },
    // 删除全景视图
    deletePanorama(id) {
      deletePanorama(id).then(res => {
        console.log(res, '全景视图删除成功！');
        if (res.success) {
          this.$message.success('删除全景图成功!');
          // 判断是否是3D界面的删除 zyp
          if (this.threeDYemian) {
            this.getPanoramaByCadId();
          } else {
            this.getPanoramaByCadId();
          }
        }
      });
    },
    destroyMap() {
      if (this.map) {
        try {
          this.map.remove();
          this.map = null;
        } catch (e) {
          console.warn('地图销毁失败', e);
        }
      }
      vjmap.clearPrewarmedResources();
    },
    async initMap(cadid, mapid) {
      this.destroyMap();
      this.fullOrfloor = false;
      try {
        // 显示加载动画
        this.loadingTrigger(true);

        this.map = null;
        const env = {
          serviceUrl: 'https://xiaowu-iot.com/cad/api/v1',
          accessToken:
            'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlcm5hbWUiOiJyb290MSIsIk5pY2tOYW1lIjoicm9vdDEiLCJBdXRob3JpdHlJZCI6InJvb3QiLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODQwOTI1ODYxLCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY4NzMyNDg2MX0.0P4hQkpOp1h7ynqjA3gMbckkG5b4PWtHSNGnWmfmJl4',
          exampleMapId: 'sys_zp',
        };
        // 地图服务对象
        const svc = new vjmap.Service(env.serviceUrl, env.accessToken);

        // 打开地图
        const res = await svc.openMap({
          mapid: mapid, // 地图ID
          mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
          style: vjmap.openMapDarkStyle(), // div为深色背景颜色时，这里也传深色背景样式
        });

        if (res.error) {
          // 如果打开出错
          console.error(res.error);
          return;
        }

        // 获取地图范围
        const mapExtent = vjmap.GeoBounds.fromString(res.bounds);

        // 根据地图范围建立几何投影坐标系
        const prj = new vjmap.GeoProjection(mapExtent);

        // 地图对象
        this.map = new vjmap.Map({
          container: 'map-container', // DIV容器ID
          style: svc.rasterStyle(), // 样式，这里是栅格样式
          center: [0, -15], // 设置地图中心点
          zoom: 3,
          maxZoom: 10, // 设置地图缩放级别,
          minZoom: 3.2,
          pitch: 50, // 倾斜角度
          renderWorldCopies: false, // 不显示多屏地图
          pitchWithRotate: false,
          dragRotate: false, // 拖动旋转
        });

        // 关联服务对象和投影对象
        this.map.attach(svc, prj);
        await this.map.onLoad(); // 等待地图加载完成

        this.map.getCanvas().style.cursor = 'default';

        // 添加 设备管理 图层
        this.deviceLayer = new vjmap.Symbol({
          iconImage: ['get', 'img'],
          iconOffset: [0, 0],
          iconAllowOverlap: true,
          iconSize: ['interpolate', ['linear'], ['zoom'], 5, 0.5, 10, 1, 15, 2, 20, 3],
          data: [],
        });
        this.deviceLayer.addTo(this.map);

        this.map.on('click', this.deviceLayer.getLayerId(), e => {
          const feature = e.features?.[0];
          console.log(feature, '打印数据');

          if (!feature) return;
          this.limitCadClickBehavior = true;
          this.currentClickProperty = 'cadDeviceClick';
          this.deviceCurrentID = feature.properties?.id;
          this.deviceObject = feature.properties?.deviceObject;
          if (this.currentViewMode == 'operation') {
          } else if (this.currentViewMode == 'editmode') {
          }
        });

        // 添加 iconLayer 图层
        this.panoramaLayer = new vjmap.Symbol({
          iconImage: ['get', 'img'],
          iconOffset: [0, 0],
          iconAllowOverlap: true,
          iconSize: ['interpolate', ['linear'], ['zoom'], 5, 0.5, 10, 1, 15, 2, 20, 3],
          data: [],
        });
        this.panoramaLayer.addTo(this.map);

        this.map.on('click', this.panoramaLayer.getLayerId(), e => {
          const feature = e.features?.[0];
          console.log(feature, '打印数据');

          if (!feature) return;
          if (this.currentViewMode == 'operation') {
            this.panoramaName = feature.properties?.text;
            this.panoramaImgPath = feature.properties?.url;
            this.panoramaCurrentID = feature.properties?.id;
            this.addCADPanoramaDialog = true;
            this.triggerType == 'panoramaData';
            this.loadPanorama(false, 'query');
            this.handlePanoramaMomeClick();
          } else if (this.currentViewMode == 'editmode') {
            this.panoramaName = feature.properties?.text;
            this.panoramaImgPath = feature.properties?.url;
            this.panoramaCurrentID = feature.properties?.id;
            this.currentClickProperty = 'cadAddPanoramaClick';
            this.limitCadClickBehavior = true;
          }
        });
        // 添加 iconLayer 图层 pop 提示弹窗信息
        let panoramaHoverPopup = null;
        this.map.on('mouseenter', this.panoramaLayer.getLayerId(), e => {
          this.map.getCanvas().style.cursor = 'pointer';

          const feature = e.features?.[0];
          if (!feature) return;

          const coordinates = feature.geometry.coordinates;
          const text = feature.properties?.text;

          if (!panoramaHoverPopup) {
            panoramaHoverPopup = new vjmap.Popup({
              closeButton: false,
              closeOnClick: false,
              offset: [0, -10],
            });
          }

          panoramaHoverPopup
            .setLngLat(coordinates)
            .setHTML(`<div style="color: #f5f5f5;">${text}</div>`)
            .addTo(this.map);
        });

        this.map.on('mouseleave', this.panoramaLayer.getLayerId(), () => {
          this.map.getCanvas().style.cursor = 'default';
          if (panoramaHoverPopup) {
            panoramaHoverPopup.remove();
          }
        });

        // 一秒一刷新数据
        // 添加 iconLayer 图层
        this.iconLayer = new vjmap.Symbol({
          iconImage: ['get', 'img'],
          iconOffset: [0, 0],
          iconAllowOverlap: true,
          iconSize: ['interpolate', ['linear'], ['zoom'], 5, 0.5, 10, 1, 15, 2, 20, 3],
          data: [],
        });
        this.iconLayer.addTo(this.map);

        this.map.on('click', this.iconLayer.getLayerId(), e => {
          const feature = e.features?.[0];
          if (!feature) return;
          // 判断是编辑模式还是运维模式，编辑模式下可以点击删除图标，不用可控
          // 运维模式下，点击控制按钮调动接口，不显示删除图标
          if (this.currentViewMode == 'operation') {
            updatePhysicalPoint({
              id: feature.properties.dataId,
              value: parseInt(feature.properties.value),
            }).then(res => {
              console.log(res, '控制成功');
              if (res.code == 200) {
                this.$message.success('控制成功');
              }
            });
          } else if (this.currentViewMode == 'editmode') {
            this.currentClickProperty = 'cadAddDataClick';
            this.limitCadClickBehavior = true;
            this.editmodeDeleteIconFlag = true;
            this.editmodeDeleteIconDataId = feature.properties.dataId;
          }
        });

        // 添加 iconLayer 图层 pop 提示弹窗信息
        let iconHoverPopup = null;
        this.map.on('mouseenter', this.iconLayer.getLayerId(), e => {
          this.map.getCanvas().style.cursor = 'pointer';

          const feature = e.features?.[0];
          if (!feature) return;

          const coordinates = feature.geometry.coordinates;
          const text = feature.properties?.text;

          if (!iconHoverPopup) {
            iconHoverPopup = new vjmap.Popup({
              closeButton: false,
              closeOnClick: false,
              offset: [0, -10],
            });
          }

          iconHoverPopup
            .setLngLat(coordinates)
            .setHTML(`<div style="color: #f5f5f5;">${text}</div>`)
            .addTo(this.map);
        });

        this.map.on('mouseleave', this.iconLayer.getLayerId(), () => {
          this.map.getCanvas().style.cursor = 'default';
          if (iconHoverPopup) {
            iconHoverPopup.remove();
          }
        });

        // 文字的数据结构
        this.sensorLayer = new vjmap.Symbol({
          type: 'symbol',
          id: 'sensor-layer',
          textId: ['get', 'id'],
          source: 'sensor-source',
          textField: ['get', 'text'],
          textFont: ['Arial Unicode MS Regular'],
          'text-size': ['interpolate', ['linear'], ['zoom'], 5, 15, 10, 20, 15, 30, 20, 48],
          textColor: ['get', 'textColor'],
          textOffset: [0, 0],
          textAnchor: 'center',
          iconAllowOverlap: true,
          textAllowOverlap: true,
          data: [],
        });
        this.sensorLayer.addTo(this.map);

        this.map.on('mouseenter', this.sensorLayer.getLayerId(), e => {
          this.map.getCanvas().style.cursor = 'pointer';
        });
        this.map.on('mouseleave', this.sensorLayer.getLayerId(), () => {
          this.map.getCanvas().style.cursor = 'default';
        });

        this.map.on('click', this.sensorLayer.getLayerId(), e => {
          const feature = e.features?.[0];
          if (feature) {
            console.log('点击了 Symbol 图层:', feature);
            this.currentClickProperty = 'cadAddDataClick';
            this.limitCadClickBehavior = true;
            this.currentClickCADAddData = JSON.parse(feature.properties.sourceData);
            console.log(this.currentClickCADAddData, '点击的 Symbol 数据');
          }
        });

        // 动态区域加载数据
        this.map.addSource('label-source', {
          type: 'geojson',
          data: {
            type: 'FeatureCollection',
            features: [],
          },
        });
        this.map.addLayer({
          id: 'label-layer',
          type: 'symbol',
          source: 'label-source',
          layout: {
            'text-field': ['get', 'label'],
            'text-size': ['interpolate', ['linear'], ['zoom'], 5, 12, 10, 18, 15, 28, 20, 40],
            'text-offset': [0, 0],
            'text-anchor': 'center',
          },
          paint: {
            'text-color': ['get', 'color'],
          },
        });

        this.map.on('mouseenter', 'label-layer', e => {
          this.map.getCanvas().style.cursor = 'pointer';

          if (e.features.length) {
            // 先清掉旧的
            if (hoveredId !== null) {
              this.map.setFeatureState({ source: 'label-source', id: hoveredId }, { hover: false });
            }
            // 设置新的
            hoveredId = e.features[0].id;
            this.map.setFeatureState({ source: 'label-source', id: hoveredId }, { hover: true });
          }
        });

        this.map.on('mouseleave', 'label-layer', () => {
          this.map.getCanvas().style.cursor = 'default';
          if (hoveredId !== null) {
            this.map.setFeatureState({ source: 'label-source', id: hoveredId }, { hover: false });
          }
          hoveredId = null;
        });

        // 查询空间ID && 查询所有已添加数据
        if (cadid) {
          this.getSpaceById(cadid);
          this.getDataByCADId(cadid, 'data');
          this.getPanoramaByCadId();
          this.selectCadDeciceData();
        }

        // 动态区域加载数据文字点击
        this.map.on('click', 'label-layer', e => {
          const feature = e.features[0];
          console.log('点击了文字:', feature);
          this.currentClickSpaceData.id = feature.properties.id;
          this.currentClickSpaceData.name = feature.properties.label;
          this.currentClickSpaceData.size = feature.properties.size;
          this.currentClickSpaceData.color = feature.properties.color;
          this.currentClickSpaceData.description = feature.properties.des;
          this.currentClickProperty = 'textClick';
          this.limitCadClickBehavior = true;
          console.log(this.currentClickSpaceData, '动态更新文字的字典集合');
          // 你可以在这里显示菜单，或执行其他逻辑
        });

        this.map.on('click', event => {
          console.log(this.currentClickProperty);
          if (this.currentClickProperty == 'addData') {
            this.addDataModalFlag = true;
            this.handleCADAddData();
            this.addCADDataForm.xAxis = event.point.x;
            this.addCADDataForm.yAxis = event.point.y;
          } else if (this.currentClickProperty == 'addPanorama') {
            this.uploadCADPanoramaFormData = new FormData();
            this.createPanorama.createShowVisiable = true;
            this.panoramaSign = 'add';
            this.panoramaDes = '添加';
            this.panoramaUploadDes = '上传';
            this.createPanorama.xAxis = event.point.x;
            this.createPanorama.yAxis = event.point.y;

            console.log(this.createPanorama.xAxis, this.createPanorama.yAxis);
          } else if (this.currentClickProperty == 'addCamera') {
            this.createCamera.createShowVisiable = true;
            this.createCamera.xAxis = event.point.x;
            this.createCamera.yAxis = event.point.y;
          } else if (this.currentClickProperty == 'addDevice') {
            this.createDevice.createShowVisiable = true;
            this.createDevice.xAxis = event.point.x;
            this.createDevice.yAxis = event.point.y;
          } else if (this.currentClickProperty == 'textClick' && this.limitCadClickBehavior) {
            // w
            this.loadPropertyData();
            setTimeout(() => {
              const hasAsset = this.tableData && this.tableData.length > 0;
              const editmodeItems = [
                {
                  type: 'multi',
                  items: [
                    {
                      label: '关联数据',
                      onClick: () => {
                        this.relevanceDialog = true;
                        this.triggerType = 'relatedData';
                      },
                      icon: require('../assets/group/guanlian.png'),
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: '备忘录',
                      onClick: () => {
                        this.getMome();
                        this.addBookmarkDialog = true;
                      },
                      icon: this.bookMarkMenuIcon,
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: hasAsset ? '编辑资产' : '添加资产',
                      onClick: () => {
                        this.assetDialog = true;
                      },
                      icon: hasAsset ? this.editAssetsMenuIcon : this.addAssetsMenuIcon,
                    },
                  ],
                },
                // w
                {
                  type: 'multi',
                  items: [
                    {
                      label: '查看资产',
                      onClick: () => {
                        this.LookDong = true;
                        this.loadPropertyData(); // 加载数据
                      },
                      icon: this.viewAssetsMenuIcon,
                    },
                  ],
                },
                // w
                {
                  type: 'multi',
                  items: [
                    {
                      label: '添加人员',
                      onClick: () => {},
                      icon: this.addPersonIcon,
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: '编辑空间ID',
                      onClick: () => {
                        this.updateSpaceData.updateSpaceId = this.currentClickSpaceData.id;
                        this.updateSpaceData.updateSpaceName = this.currentClickSpaceData.name;
                        this.updateSpaceData.updateSpaceDescribe =
                          this.currentClickSpaceData.description;
                        this.updateSpaceData.updateSpaceFontSize = this.currentClickSpaceData.size;
                        this.updateSpaceData.updateSpaceColor = this.currentClickSpaceData.color;
                        this.updateSpaceData.updateSpaceIDDialog = true;
                      },
                      icon: require('../assets/group/edit.png'),
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: '删除空间ID',
                      onClick: () => {
                        this.deleteSpaceDialog = true;
                      },
                      icon: require('../assets/group/delete.png'),
                    },
                  ],
                },
              ];
              const operationItems = [
                {
                  type: 'multi',
                  items: [
                    {
                      label: '备忘录',
                      onClick: () => {
                        this.getMome();
                        this.addBookmarkDialog = true;
                      },
                      icon: this.bookMarkMenuIcon,
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: '资产',
                      onClick: () => {},
                      icon: this.addAssetsMenuIcon,
                    },
                  ],
                },
                {
                  type: 'multi',
                  items: [
                    {
                      label: '操作',
                      onClick: () => {},
                      icon: this.bookMarkMenuIcon,
                    },
                  ],
                },
              ];
              const contextMenu = new vjmap.ContextMenu({
                event: event.originalEvent,
                theme: 'dark', // 设置主题（dark/light）
                width: '116px', // 设置菜单宽度
                position: { x: event.clientX, y: event.clientY }, // 使用屏幕坐标定位菜单
                items: this.currentViewMode == 'editmode' ? editmodeItems : operationItems,
              });
              // 显示自定义的 ContextMenu
              contextMenu.show();
              this.limitCadClickBehavior = false;
            }, 100);
            // w
          } else if (this.currentClickProperty == 'cadAddDataClick' && this.limitCadClickBehavior) {
            const operationItems = [
              {
                type: 'multi',
                items: [
                  {
                    label: '删除数据',
                    onClick: () => {
                      console.log(this.currentClickCADAddData, '点击的当前添加数据');
                      if (this.editmodeDeleteIconFlag) {
                        this.deleteData(this.recordRightTreeNodeId, this.editmodeDeleteIconDataId);
                        this.editmodeDeleteIconFlag = false;
                      } else {
                        this.deleteData(
                          this.currentClickCADAddData.cadId,
                          this.currentClickCADAddData.dataId
                        );
                      }
                    },
                    icon: require('../assets/group/delete.png'),
                  },
                ],
              },
            ];
            const contextMenu = new vjmap.ContextMenu({
              event: event.originalEvent,
              theme: 'dark', // 设置主题（dark/light）
              width: '146px', // 设置菜单宽度
              position: { x: event.clientX, y: event.clientY }, // 使用屏幕坐标定位菜单
              items: operationItems,
            });
            // 显示自定义的 ContextMenu
            contextMenu.show();
            this.limitCadClickBehavior = false;
          } else if (
            this.currentClickProperty == 'cadAddPanoramaClick' &&
            this.limitCadClickBehavior
          ) {
            const operationItems = [
              {
                type: 'multi',
                items: [
                  {
                    label: '编辑视图',
                    onClick: () => {
                      this.panoramaSign = 'edit';
                      this.panoramaDes = '编辑';
                      this.panoramaUploadDes = '更新';
                      this.editCadPanorama();
                    },
                    icon: require('../assets/group/edit2.png'),
                  },
                ],
              },
              {
                type: 'multi',
                items: [
                  {
                    label: '查看视图',
                    onClick: () => {
                      this.addCADPanoramaDialog = true;
                      this.triggerType == 'panoramaData';
                      this.loadPanorama(false, 'query');
                      this.handlePanoramaMomeClick();
                    },
                    icon: require('../assets/group/show.png'),
                  },
                ],
              },
              {
                type: 'multi',
                items: [
                  {
                    label: '删除视图',
                    onClick: () => {
                      this.deletePanorama(this.panoramaCurrentID);
                    },
                    icon: require('../assets/group/delete.png'),
                  },
                ],
              },
            ];
            const contextMenu = new vjmap.ContextMenu({
              event: event.originalEvent,
              theme: 'dark', // 设置主题（dark/light）
              width: '106px', // 设置菜单宽度
              position: { x: event.clientX, y: event.clientY }, // 使用屏幕坐标定位菜单
              items: operationItems,
            });
            // 显示自定义的 ContextMenu
            contextMenu.show();
            this.limitCadClickBehavior = false;
          } else if (this.currentClickProperty == 'cadDeviceClick' && this.limitCadClickBehavior) {
            const operationItems = [
              {
                type: 'multi',
                items: [
                  {
                    label: '查看模板',
                    onClick: () => {
                      this.$refs.threeDialog.openThreeDialog(JSON.parse(this.deviceObject));
                    },
                    icon: require('../assets/group/edit2.png'),
                  },
                ],
              },
              {
                type: 'multi',
                items: [
                  {
                    label: '删除模板',
                    onClick: () => {
                      this.deleteCadDevice(this.deviceCurrentID);
                    },
                    icon: require('../assets/group/show.png'),
                  },
                ],
              },
            ];
            const contextMenu = new vjmap.ContextMenu({
              event: event.originalEvent,
              theme: 'dark', // 设置主题（dark/light）
              width: '106px', // 设置菜单宽度
              position: { x: event.clientX, y: event.clientY }, // 使用屏幕坐标定位菜单
              items: operationItems,
            });
            // 显示自定义的 ContextMenu
            contextMenu.show();
            this.limitCadClickBehavior = false;
          }
        });
        // 设置右键菜单
        this.setMapMenu();
      } catch (error) {
        console.error('地图加载失败:', error);
        this.isMapLoading = false;
      } finally {
        // 隐藏加载动画
        this.loadingTrigger(false);
      }
    },
    getSpaceById(id) {
      // 请求当前map地图空间ID信息
      getSpaceById(id).then(res => {
        this.labelFeatures = [];
        console.log(res, 'map 空间ID信息');
        this.spaceData = res.data;

        const features = this.spaceData.map(item => {
          const position = { x: item.x, y: item.y };
          const lngLat = this.map.toLngLat(position);
          return {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: [lngLat[0], lngLat[1]],
            },
            properties: {
              id: item.id,
              label: item.name,
              color: item.color,
              size: parseInt(item.size),
              des: item.description || '',
              source: 'api', // 标记来自后端加载
            },
          };
        });

        console.log(features, 'reatures');

        // 更新 label-source 数据
        const source = this.map.getSource('label-source');
        if (source) {
          source.setData({
            type: 'FeatureCollection',
            features,
          });
        }

        // 可选：同步保存一份 Feature 以便后续增删查改
        this.labelFeatures = features;
      });
    },
    getDataByCADId(cadid, type) {
      this.sensorTimer && clearInterval(this.sensorTimer);
      this.sensorTimer = setInterval(() => {
        getDataByCADId(cadid, type).then(res => {
          // console.log(res, '获取到的cad所有数据-每秒都在获取');
          const sensorFeatures = [];
          const graphicsFeatures = [];

          res.data.forEach(item => {
            const { x, y, type, value, internalDataModel } = item;

            if (type === 'DATA') {
              sensorFeatures.push({
                type: 'Feature',
                geometry: {
                  type: 'Point',
                  coordinates: [x, y],
                },
                properties: {
                  id: item.id,
                  text: `${internalDataModel.name}${value}${internalDataModel.unit}`,
                  textColor: internalDataModel.color,
                  textSize: internalDataModel.size,
                  sourceData: item,
                },
              });
            }

            if (type === 'TEXT') {
              const textFlag = parseInt(value);
              const textContent = textFlag ? internalDataModel.text0 : internalDataModel.text1;
              const textColor = textFlag ? internalDataModel.color0 : internalDataModel.color1;
              const textSize = textFlag ? internalDataModel.size0 : internalDataModel.size1;

              sensorFeatures.push({
                type: 'Feature',
                geometry: {
                  type: 'Point',
                  coordinates: [x, y],
                },
                properties: {
                  id: item.id,
                  text: textContent,
                  textColor: textColor,
                  textSize: textSize,
                },
              });
            }

            if (type === 'GRAPHICS') {
              const iconIndex = internalDataModel.style;
              const iconFlag = parseInt(value);
              const img = this.addPanoramaButtonData.iconArray?.[iconIndex]?.[iconFlag] || '';

              if (img) {
                graphicsFeatures.push({
                  type: 'Feature',
                  geometry: {
                    type: 'Point',
                    coordinates: [x, y],
                  },
                  properties: {
                    id: item.id,
                    dataId: item.dataId,
                    value: iconFlag ? 0 : 1,
                    iconIndex: iconIndex,
                    img: img,
                    text: internalDataModel.name,
                  },
                });

                // 如果图标未加载，则预加载
                if (!this.map.hasImage(img)) {
                  const image = new Image();
                  image.crossOrigin = 'anonymous';
                  image.src = img;
                  image.onload = () => {
                    if (!this.map.hasImage(img)) {
                      this.map.addImage(img, image);
                    }
                  };
                }
              }
            }
          });
          // 更新 sensorLayer
          this.sensorLayer.setData({
            type: 'FeatureCollection',
            features: sensorFeatures,
          });

          // 更新 iconLayer（GRAPHICS 图层，支持增删查）
          this.iconLayer.setData({
            type: 'FeatureCollection',
            features: graphicsFeatures,
          });
        });
      }, 1000);
    },
    // 删除添加的数据
    deleteData(cadId, dataId) {
      deleteData({ cadId, dataId }).then(res => {
        console.log(res, '删除数据成功');
        this.$message.success('数据删除成功');
        this.getDataByCADId(this.recordRightTreeNodeId, 'data');
      });
    },
    // 自定义右键菜单
    setMapMenu() {
      this.threeDYemian = false; // zyp
      this.map.setMenu(event => {
        if (this.currentViewMode !== 'editmode') return null;

        event.originalEvent.preventDefault();
        this.eventMap = event;

        // w
        // this.loadPropertyData();
        setTimeout(() => {
          const hasAsset = this.tableData && this.tableData.length;

          const editmodeItems = [
            {
              label: '创建空间ID',
              onClick: () => {
                this.handleSpaceIDClick();
              },
              icon: this.createInstance,
            },
            {
              label: '添加数据',
              onClick: () => {
                this.currentClickProperty = 'addData';
                this.triggerType = 'chooseData';
              },
              icon: this.addDataMenuIcon,
            },
            {
              label: '添加摄像机',
              onClick: () => {
                this.createCameraSign = 'cad';
                this.currentClickProperty = 'addCamera';
              },
              icon: this.videoMenuIcon,
            },
            // w
            // {
            //   label: hasAsset ? '编辑资产' : '添加资产',
            //   onClick: () => {
            //     this.assetDialog = true;
            //   },
            //   icon: hasAsset ? this.editAssetsMenuIcon : this.addAssetsMenuIcon,
            // },
            // {
            //   label: '查看资产',
            //   onClick: () => {
            //     this.LookDong = true;
            //     this.loadPropertyData(); // 加载数据
            //   },
            //   icon: this.viewAssetsMenuIcon,
            // },
            // w
            {
              label: '设备模板',
              onClick: () => {
                this.currentClickProperty = 'addDevice';
              },
              icon: this.templateMenuIcon,
            },
            {
              label: '全景视图',
              onClick: () => {
                this.currentClickProperty = 'addPanorama';
              },
              icon: this.panoramicViewIcon,
            },
          ];

          return new vjmap.ContextMenu({
            event: event.originalEvent,
            theme: 'dark',
            width: '120px',
            position: { x: event.clientX, y: event.clientY },
            items: editmodeItems,
          });
        }, 100);
      });
    },
    toggleView() {
      if (this.isTopView) {
        this.map.flyTo({
          pitch: 0, // 角度（0° 表示平面图）
          bearing: 0, // 水平方向角度
          zoom: this.map.getZoom(), // 保持当前缩放级别
        });
      } else {
        this.map.flyTo({
          pitch: 50, // 角度（60° 表示俯视图）
          bearing: 0, // 旋转角度
          zoom: this.map.getZoom(), // 保持当前缩放级别
        });
      }
      this.isTopView = !this.isTopView; // 切换状态
    },
    // 添加视频
    addVideo(iconIndex, x, y, text) {
      const el = document.createElement('div');
      el.className = 'camers-marker';

      // 图标部分
      const iconEl = document.createElement('div');
      const iconUrl = this.createCamera.createIconArray[iconIndex];
      iconEl.style.backgroundImage = `url('${iconUrl}')`;
      iconEl.className = 'camers';
      el.appendChild(iconEl);

      // 文字部分
      const labelEl = document.createElement('div');
      labelEl.className = 'camers-label';
      labelEl.textContent = text; // 你想展示的小字
      el.appendChild(labelEl);
      const marker = new vjmap.Marker({
        color: vjmap.randomColor(),
        draggable: false,
        disableDragButton: 'middle,right',
        element: el,
      });
      const latLng = this.map.unproject([x, y]);
      marker.setLngLat(latLng).addTo(this.map);
      marker.on('click', () => {
        this.camerOpen(marker);
      });
      this.createCamera.createShowVisiable = false;
      this.createCamera.createIconIndex = void 0;
      this.createCamera.createCameraSerial = '';
      // this.createCamera.createCameraPath = '';
      this.currentClickProperty = '';
    },
    // 展示视频
    camerOpen(marker) {
      const popup = new vjmap.Popup({
        className: 'video-popup',
      });
      popup.setHTML(`
      <webview id="cameraShowView"
            src=${this.createCamera.createCameraPath}
            style="width:450px; height:300px; position:absolute; z-index:1;" allowpopups></webview>
      `);
      marker.setPopup(popup);
      marker.togglePopup();
    },
    // 添加设备模板
    addDevice(iconIndex, x, y) {
      const iconEl = document.createElement('div');
      const iconUrl = this.createDevice.createIconArray[iconIndex];
      iconEl.style.backgroundImage = `url('${iconUrl}')`;
      iconEl.className = 'devices';

      const marker = new vjmap.Marker({
        color: vjmap.randomColor(),
        draggable: false,
        disableDragButton: 'middle,right',
        element: iconEl,
      });
      const latLng = this.map.unproject([x, y]);
      marker.setLngLat(latLng).addTo(this.map);
      marker.on('click', () => {
        this.$refs.fan.openDialog();
      });
      this.createDevice.createShowVisiable = false;
      this.createDevice.createIconIndex = void 0;
      this.currentClickProperty = '';
    },
    panoramaOpen() {},
    handleCADAddData(params) {
      this.addCADDataDialog = true;
      if (params) {
        this.showEffectText = '图片';
        this.showAddDataFlag = false;
      } else {
        this.showEffectText = '图形';
        this.showAddDataFlag = true;
      }
    },
    handleAddDataClick() {
      console.log('当前平台添加的数据是：', this.addCADDataForm);
      if (this.showAddDataFlag) {
        const lngLat = this.eventMap.lngLat;
        const commonData = {
          cadId: this.currentTreeNode.id,
          dataId: this.addCADDataForm.chooseTreeDataId,
          x: lngLat.lng,
          y: lngLat.lat,
          redisKey: this.addCADDataForm.redisKey,
          mqttId: this.addCADDataForm.mqttId,
        };
        let requestData = {
          ...commonData,
          displayType: '',
          internalDataModel: {},
        };
        if (this.addCADDataForm.showEffect === 'numerical') {
          requestData.displayType = 'data';
          requestData.internalDataModel = {
            name: this.addCADDataForm.numerical.designation,
            unit: this.addCADDataForm.numerical.unit,
            size: this.addCADDataForm.numerical.fontSize,
            color: this.addCADDataForm.numerical.fontColor,
          };
        } else if (this.addCADDataForm.showEffect === 'picture') {
          requestData.displayType = 'graphics';
          requestData.internalDataModel = {
            style: this.addCADDataForm.picture.status,
            name: this.addCADDataForm.picture.btnShowTips,
          };
        } else if (this.addCADDataForm.showEffect === 'textArea') {
          requestData.displayType = 'text';
          requestData.internalDataModel = {
            text0: this.addCADDataForm.textArea.one.text,
            text1: this.addCADDataForm.textArea.two.text,
            size0: this.addCADDataForm.textArea.one.fontSize,
            size1: this.addCADDataForm.textArea.two.fontSize,
            color0: this.addCADDataForm.textArea.one.color,
            color1: this.addCADDataForm.textArea.two.color,
            flash0: this.addCADDataForm.textArea.one.flicker,
            flash1: this.addCADDataForm.textArea.two.flicker,
          };
        }
        console.log(requestData, 'requestData');
        createAddData(requestData).then(res => {
          if (res.success) {
            console.log(res);
            this.$message.success('数据添加成功！');
            getDataByCADId(this.recordRightTreeNodeId, 'data');
            // 添加数据和添加空间ID一样
            // 重置数据
            Object.assign(this.addCADDataForm, this.$options.data().addCADDataForm, {
              showEffect: 'numerical',
            });
          } else {
            this.$message.error('添加失败, 请检查');
          }
        });
      } else {
        console.log(this.addCADDataForm, '查询全景图添加的数据平台数据');
        if (this.addPanoramaDataMark == 'markData') {
          this.triggerType = 'panoramaData';
          if (this.addCADDataForm.showEffect == 'numerical') {
            const formData = new FormData();

            formData.set('panoramaId', this.panoramaCurrentID);
            formData.set('type', 'data');

            formData.set(
              'makerModel',
              JSON.stringify({
                type: 'number',
                dadaId: this.addCADDataForm.dataId,
                mqttId: this.addCADDataForm.mqttId,
                redisKey: this.addCADDataForm.redisKey,
                name: this.addCADDataForm.numerical.designation,
                unit: this.addCADDataForm.numerical.unit,
                size: this.addCADDataForm.numerical.fontSize,
                color: this.addCADDataForm.numerical.fontColor,
              })
            );

            formData.set('x', this.panoramaLongitude);
            formData.set('y', this.panoramaLatitude);
            formData.set('z', '');

            formData.set('file', new Blob(), 'empty.txt');

            console.log('FormData:', formData);
            postPanoramaAddmaker(formData).then(res => {
              if (res.success) {
                this.$message.success('添加数据成功');
                getPanoramaDataBySpaceId({
                  panoramaId: this.panoramaCurrentID,
                  type: 'data',
                }).then(getDataRes => {
                  console.log(getDataRes, 'resres');
                  const item = getDataRes.data.at(-1);
                  const transItem = item.makerModel;
                  const contentText = transItem.name + ':' + transItem.value + transItem.unit;
                  const color = transItem.color;
                  const size = transItem.size;
                  let addMarkers = {
                    id: `text_${item.id}`,
                    longitude: item.x,
                    latitude: item.y,
                    html: `
                    <div class="text-marker" data-id="${item.id}" style="position: relative;">
                      ${contentText}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                    anchor: 'center center',
                    style: {
                      color: color,
                      fontSize: size + 'px',
                      fontFamily: 'Helvetica, sans-serif',
                    },
                    tooltip: '',
                  };
                  this.markersPlugin.addMarker(addMarkers);
                });
              }
              // 重置表单字段
              this.addCADDataForm.dataId = '';
              this.addCADDataForm.mqttId = '';
              this.addCADDataForm.redisKey = '';
              this.addCADDataForm.numerical.designation = '';
              this.addCADDataForm.numerical.unit = '';
              this.addCADDataForm.numerical.fontSize = '11';
              this.addCADDataForm.numerical.fontColor = '#64c8fc';
            });
          } else if (this.addCADDataForm.showEffect == 'textArea') {
            console.log(this.addCADDataForm, '查询全景图添加的数据');
            const formData = new FormData();
            formData.set('type', 'data');
            formData.set(
              'makerModel',
              JSON.stringify({
                type: 'word',
                dadaId: this.addCADDataForm.dataId,
                mqttId: this.addCADDataForm.mqttId,
                redisKey: this.addCADDataForm.redisKey,
                text0: this.addCADDataForm.textArea.one.text,
                size0: this.addCADDataForm.textArea.one.fontSize,
                color0: this.addCADDataForm.textArea.one.color,
                flash0: this.addCADDataForm.textArea.one.flicker,
                text1: this.addCADDataForm.textArea.two.text,
                size1: this.addCADDataForm.textArea.two.fontSize,
                color1: this.addCADDataForm.textArea.two.color,
                flash1: this.addCADDataForm.textArea.two.flicker,
              })
            );
            formData.set('panoramaId', this.panoramaCurrentID);
            formData.set('x', this.panoramaLongitude);
            formData.set('y', this.panoramaLatitude);
            formData.set('z', '');
            formData.set('file', new Blob(), 'empty.txt');

            postPanoramaAddmaker(formData).then(res => {
              if (res.success) {
                this.$message.success('添加数据成功');
                getPanoramaDataBySpaceId({
                  panoramaId: this.panoramaCurrentID,
                  type: 'data',
                }).then(getDataRes => {
                  console.log(getDataRes, 'resres');
                  const item = getDataRes.data.at(-1);
                  const transItem = item.makerModel;
                  const currentSign = parseInt(transItem.value);
                  const contentText = currentSign ? transItem.text1 : transItem.text0;
                  const color = currentSign ? transItem.color1 : transItem.color0;
                  const size = currentSign ? transItem.size1 : transItem.size0;
                  let addMarkers = {
                    id: `text_${item.id}`,
                    longitude: item.x,
                    latitude: item.y,
                    html: `
                    <div class="text-marker" data-id="${item.id}" style="position: relative;">
                      ${contentText}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                    anchor: 'center center',
                    style: {
                      color: color,
                      fontSize: size + 'px',
                      fontFamily: 'Helvetica, sans-serif',
                    },
                    tooltip: '',
                  };
                  this.markersPlugin.addMarker(addMarkers);
                  this.addCADDataForm.dataId = '';
                  this.addCADDataForm.mqttId = '';
                  this.addCADDataForm.redisKey = '';
                  this.addCADDataForm.textArea.one.text = '';
                  this.addCADDataForm.textArea.one.fontSize = 11;
                  this.addCADDataForm.textArea.one.color = '#64c8fc';
                  this.addCADDataForm.textArea.one.flicker = false;
                  this.addCADDataForm.textArea.two.text = '';
                  this.addCADDataForm.textArea.two.fontSize = 11;
                  this.addCADDataForm.textArea.two.color = '#64c8fc';
                  this.addCADDataForm.textArea.two.flicker = false;
                });
              }
            });
          }
        }
      }
      this.addCADDataDialog = false;
    },
    // 全景图添加文本数据
    handleAddPanoramaTextData() {
      const formData = new FormData();
      formData.set('panoramaId', this.panoramaCurrentID);
      if (this.operationTextSign == 'edit') {
        formData.set('id', this.operationTextId);
      }
      formData.set('type', 'text');

      formData.set(
        'makerModel',
        JSON.stringify({
          text: this.addPanoramaTextData.text,
          link: this.addPanoramaTextData.link,
          size: this.addPanoramaTextData.fontSize + 'px',
          color: this.addPanoramaTextData.fontColor,
        })
      );
      if (this.operationTextSign == 'edit') {
        formData.set('x', this.operationTextX);
        formData.set('y', this.operationTextY);
      } else {
        formData.set('x', this.panoramaLongitude);
        formData.set('y', this.panoramaLatitude);
      }
      formData.set('z', '');

      // 添加空的 file 字段
      formData.set('file', new Blob(), 'empty.txt');
      // 进行判段 zyp
      console.log(this.panoramaLongitude, this.panoramaLatitude, '获取到的点信息');
      if (this.operationTextSign == 'edit') {
        postPanoramaAddmaker(formData).then(res1 => {
          if (res1.success) {
            this.$message.success('更新文本数据成功');
            this.addPanoramaTextDialog = false;
            getPanoramaDataBySpaceId({
              panoramaId: this.panoramaCurrentID,
              type: 'text',
            }).then(res => {
              if (res.success && Array.isArray(res.data)) {
                const item = res.data.find(i => String(i.id) === String(this.operationTextId));
                console.log(item, 'itemitemitem');

                if (item?.makerModel) {
                  try {
                    const transItem = item.makerModel;
                    const path = transItem.link;
                    const marker = {
                      id: `text_${item.id}`,
                      longitude: item.x,
                      latitude: item.y,
                      html: `
                    <div class="text-marker plain-text" data-id="${item.id}" data-path="${path}">
                      ${transItem.text}
                      <span class="text-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
                      anchor: 'center center',
                      style: {
                        color: transItem.color,
                        fontSize: transItem.size,
                        fontFamily: 'Helvetica, sans-serif',
                      },
                      tooltip: '',
                    };
                    this.addOrUpdateMarker(marker);
                    this.addPanoramaTextData.text = '';
                    this.addPanoramaTextData.link = '';
                    this.addPanoramaTextData.fontSize = '11px';
                    this.addPanoramaTextData.fontColor = 'rgba(38, 38, 38, 1)';
                  } catch (e) {
                    console.error('解析失败:', e);
                  }
                }
              }
            });
          }
        });
      } else {
        postPanoramaAddmaker(formData).then(res => {
          if (res.success) {
            this.$message.success('添加文本数据成功');
            getPanoramaDataBySpaceId({
              panoramaId: this.panoramaCurrentID,
              type: 'text',
            }).then(getTextRes => {
              console.log(getTextRes, 'resres');
              const id = getTextRes.data.at(-1).id;
              const marker = {
                id: `text_${id}`,
                longitude: this.panoramaLongitude,
                latitude: this.panoramaLatitude,
                html: `
                <div class="text-marker plain-text" data-id="${id}" data-path="${this.addPanoramaTextData.link}">
                  ${this.addPanoramaTextData.text}
                  <span class="text-marker-close" data-id="${id}">×</span>
                </div>
              `,
                anchor: 'center center',
                style: {
                  color: this.addPanoramaTextData.fontColor,
                  fontSize: this.addPanoramaTextData.fontSize + 'px',
                  fontFamily: 'Helvetica, sans-serif',
                },
                tooltip: '',
              };
              this.markersPlugin.addMarker(marker);

              this.addPanoramaTextData.text = '';
              this.addPanoramaTextData.link = '';
              this.addPanoramaTextData.fontSize = '11px';
              this.addPanoramaTextData.fontColor = 'rgba(38, 38, 38, 1)';
              this.addPanoramaTextDialog = false;
            });
          }
        });
      }
    },
    handleUploadPanoramaPicture() {
      this.$refs.filePanoramaInput.click();
    },
    handleFilePanoramaUpload(event) {
      const file = event.target.files[0];
      this.uploadPanoramaDataImageFormData = new FormData();
      this.uploadPanoramaDataImageFormData.set('panoramaId', this.panoramaCurrentID);
      this.uploadPanoramaDataImageFormData.set('type', 'graphics');
      this.uploadPanoramaDataImageFormData.set('x', this.panoramaLongitude);
      this.uploadPanoramaDataImageFormData.set('y', this.panoramaLatitude);
      this.uploadPanoramaDataImageFormData.set('z', '');
      this.uploadPanoramaDataImageFormData.set('file', file);
    },
    handleAddPanoramaImage() {
      this.uploadPanoramaDataImageFormData.set(
        'makerModel',
        JSON.stringify({ outLink: this.addPanoramaImagePath })
      );
      postPanoramaAddmaker(this.uploadPanoramaDataImageFormData).then(res => {
        console.log(res, '图片添加成功！');
        if (res.success) {
          this.$message.success('添加图片成功');
          getPanoramaDataBySpaceId({
            panoramaId: this.panoramaCurrentID,
            type: 'graphics',
          }).then(getImageRes => {
            console.log(getImageRes, 'resres');
            if (getImageRes.success) {
              const id = getImageRes.data.at(-1).id;
              const transItem = getImageRes.data.at(-1).makerModel;
              let path = transItem.outLink;
              const marker = {
                id: `img_${id}`,
                longitude: this.panoramaLongitude,
                latitude: this.panoramaLatitude,
                html: `
                    <div class="img-marker" data-path="${path}" data-id="${id}" style="position: relative;">
                    <img src="${transItem.link}"
                    alt="${transItem.fileName || 'marker image'}"
                    style="width: auto; height: auto; border-radius: 6px;" />
                    <span class="img-marker-close" data-id="${id}">×</span>
                    </div>
                  `,
                anchor: 'center center',
                style: {},
                tooltip: '',
              };
              this.markersPlugin.addMarker(marker);
            }
          });
          this.addPanoramaPictrue = false;
          this.addPanoramaImagePath = '';
        }
      });
    },
    // 全景图添加按钮样式确定
    handlePanoramaAddBtnClick() {
      console.log(this.addPanoramaButtonData);
      const formData = new FormData();
      formData.set('panoramaId', this.panoramaCurrentID);
      formData.set('type', 'data');

      formData.set(
        'makerModel',
        JSON.stringify({
          type: 'button',
          dataId: this.addPanoramaButtonData.dataId,
          mqttId: this.addPanoramaButtonData.mqttId,
          redisKey: this.addPanoramaButtonData.redisKey,
          name: this.addPanoramaButtonData.description,
          buttonType: this.addPanoramaButtonData.selectIconIndex,
        })
      );
      formData.set('x', this.panoramaLongitude);
      formData.set('y', this.panoramaLatitude);
      formData.set('z', '');

      postPanoramaAddmaker(formData).then(res => {
        console.log(res);
        if (res.success) {
          this.$message.success('添加全景图按钮成功');
          getPanoramaDataBySpaceId({
            panoramaId: this.panoramaCurrentID,
            type: 'data',
          }).then(getDataRes => {
            const item = getDataRes.data.at(-1);
            const transBtnItem = item.makerModel;
            const iconIndex = transBtnItem.buttonType;
            const iconTips = transBtnItem.name;
            const iconFlag = parseInt(transBtnItem.value);
            const iconControlId = transBtnItem.dataId;
            let addMarkers = {
              id: `img_${item.id}`,
              longitude: item.x,
              latitude: item.y,
              html: `
                    <div class="button-control-marker" data-control-status="${iconFlag}" data-control-id="${iconControlId}" data-type="button" title="${iconTips}" data-id="${item.id}" style="position: relative;">
                      <img src="${this.addPanoramaButtonData.iconArray[iconIndex][iconFlag]}"
                          style="width: 18px; height: 18px" />
                      <span class="img-marker-close" data-id="${item.id}">×</span>
                    </div>
                  `,
              anchor: 'center center',
              style: {},
              tooltip: '',
            };
            this.markersPlugin.addMarker(addMarkers);
          });
          this.addPanoramaButtonData.chooseFrontDataName = '';
          this.addPanoramaButtonData.dataId = '';
          this.addPanoramaButtonData.mqttId = '';
          this.addPanoramaButtonData.redisKey = '';
          this.addPanoramaButtonData.description = '';
          this.addPanoramaButtonData.selectIconIndex = void 0;
        }
      });
      this.addPanoramaButtonData.visiable = false;
    },
    // 全景图添加一个按钮选择图标
    handleAddPanoramaChooseIconClick(index, sign) {
      if (sign == 'addData') {
        this.addCADDataForm.picture.status = index;
      } else if (sign == 'addPanorama') {
        this.addPanoramaButtonData.selectIconIndex = index;
      }
    },
    // 全景图关闭按钮
    handlePanoramaButtonClose() {
      this.addPanoramaButtonData.visiable = false;
    },
    handlePanoramaBtnClick() {},
    loadingTrigger(status = false) {
      status ? (this.loading = true) : (this.loading = false);
      status ? (this.isMapLoading = true) : (this.isMapLoading = false);
    },
    handleTreeClose() {
      this.treeDialog = false;
    },
    handleTreeDialogCheckChange(data, checked, indeterminate) {
      console.log(data, checked, indeterminate);
    },
    handleTreeControlClose() {
      this.treeLevelControlVisiable = false;
    },
    handleAddCADDataClose() {
      this.addCADDataDialog = false;
      this.setMapMenu();
      this.currentClickProperty = '';
    },
    handleAddCADPanoramaClose() {
      this.addCADPanoramaDialog = false;
      this.clearPanorama();
    },
    handleEditCADDataClose() {
      this.uploadCADPanoramaDialog = false;
    },
    handleEditIconClick() {
      this.uploadCADPanoramaDialog = true;
    },
    handlePanoramaTextClose() {
      this.addPanoramaTextDialog = false;
    },
    handlePanoramaPictrueClose() {
      this.addPanoramaPictrue = false;
    },
    handleRightFloatClick() {
      this.rightFloatView = !this.rightFloatView;
    },
    // 看板关闭dialog
    handleAddBoardDataClose() {
      this.boardDataForm.visiable = false;
    },
    handleAddBoardDataClick() {
      console.log(this.boardDataForm, '查看看板添加数据');
      const formData = new FormData();
      formData.set('dataId', this.boardDataForm.dataId);
      formData.set('redisKey', this.boardDataForm.redisKey);
      formData.set('mqttId', this.boardDataForm.mqttId);
      formData.set('name', this.boardDataForm.designation);
      formData.set('x', this.boardDataForm.x);
      formData.set('y', this.boardDataForm.y);
      formData.set('size', this.boardDataForm.size);
      formData.set('color', this.boardDataForm.color);
      formData.set('controllabe', false);
      formData.set('cadTreeId', this.treeLevelCurrentId);
      postDisplayData(formData).then(res => {
        console.log(res, '看板添加数据返回的res');
      });
    },
    topBasicsClick(item) {
      console.log(item);
      if (item.model == 'editmode') {
        this.currentViewMode = 'operation';
        this.toolbarData.toolbarCurrentModel = toolbarArray[0];
        this.showAngleView = false;
        //  zyp
        if (this.controls && this.onControlsChange) {
          this.controls.removeEventListener('change', this.onControlsChange);
          this.onControlsChange = null;
        }
      } else if (item.model == 'operation') {
        this.currentViewMode = 'editmode';
        this.toolbarData.toolbarCurrentModel = toolbarArray[1];
      } else if (item.model == 'energy') {
        // return;
        if (item.text == '饼状图') {
          this.currentShowDisplay = 'pie';
          this.handleModel(1);
        }
        if (item.text == '柱状图') {
          this.currentShowDisplay = 'bar';
          this.handleModel(2);
        }
        if (item.text == '曲线图') {
          this.currentShowDisplay = 'line';
          this.handleModel(3);
        }
        if (item.text == '时间表') {
          this.handleModel(4);
        }
        if (item.text == '页面链接') {
          this.handleModel(5);
          this.showAddButtonDialog = true;
          this.$refs.addbtnbox.addBtn();
        }
        if (item.text == '数据') {
          this.globalTopDataSign = 'data';
          this.triggerType = 'boardAddData';
          this.boardDataForm.visiable = true;
        }
        if (item.text == '文本') {
          this.handleModel(6);
        }
        if (item.text == '时间表') {
          this.$refs.timeChartRef.openTimeDialog();
        }
        if (item.text == '图片') {
          this.$refs.displayAddImage.openVisiable();
        }
        return;
      }
    },
    handlePropertybarClick(item, index) {
      switch (item.text) {
        case '设备管理':
          ipcRenderer.send('open-front');
          break;
        case '人员管理':
          this.currentPropertyIndex = index;
          this.mainToggle = false;
          this.$router.push(`/visual/personnel`);
          this.propertybarArray[0].img = this.projectUnActiveIcon;
          this.propertybarArray[4].img = this.peopleActiveIcon;
          break;
        case '资产管理':
          ipcRenderer.send('open-property-window'); // 发送打开新窗口命令
          break;
        case '能源管理':
          // this.mainToggle = false;
          // this.$router.push(`/visual/energy`);
          break;
        case '项目':
          this.currentPropertyIndex = index;
          this.mainToggle = true;
          this.$router.push(`/visual`);
          this.propertybarArray[0].img = require('../assets/menu/xiangmutubiao.png');
          this.propertybarArray[4].img = require('../assets/menu/people.png');
          this.clearCompleteModel();
          this.getRightTree();
          break;
        default:
          break;
      }
    },
    handleBookmarkClose() {
      this.memoText = '';
      this.addBookmarkDialog = false;
    },
    handleSpaceIDClick() {
      this.createSpaceData.spaceIDDialog = true;
    },
    handleSpaceIDDialogClose() {
      this.createSpaceData.spaceIDDialog = false;
    },
    handleSpaceBtnClick() {
      const point = this.map.fromLngLat(this.eventMap.lngLat);
      console.log(this.currentTreeNode);
      saveSpace({
        cadId: this.currentTreeNode.id,
        name: this.createSpaceData.spaceIDName,
        description: this.createSpaceData.spaceDescribe,
        color: this.createSpaceData.spaceFontColor,
        size: this.createSpaceData.spaceFontSize,
        x: point.x,
        y: point.y,
        dataId: '', //数据ID
        redisKey: '', //前缀
        mqttId: '',
      }).then(res => {
        if (res.success) {
          getSpaceById(this.currentTreeNode.id).then(res => {
            const id = res.data.at(-1).id;
            const labelFeature = {
              type: 'Feature',
              geometry: {
                type: 'Point',
                coordinates: [this.eventMap.lngLat.lng, this.eventMap.lngLat.lat],
              },
              properties: {
                label: this.createSpaceData.spaceIDName,
                color: this.createSpaceData.spaceFontColor || '#64c8fc',
                size: parseInt(this.createSpaceData.spaceFontSize),
                des: this.createSpaceData.spaceDescribe,
                id,
              },
            };

            const source = this.map.getSource('label-source');
            if (!source) return;

            // 获取当前数据（深拷贝避免直接修改原数据）
            const oldData = source._data || {
              type: 'FeatureCollection',
              features: [],
            };
            const newFeatures = oldData.features.concat(labelFeature);

            // 重新设置数据刷新图层
            source.setData({
              type: 'FeatureCollection',
              features: newFeatures,
            });

            this.createSpaceData = { ...this.$options.data().createSpaceData };
            this.handleSpaceIDDialogClose();
          });
        } else {
          this.$message.error('新增空间ID失败，请检查');
        }
      });
    },

    // 关联数据关闭弹窗
    handleRelevanceDialogClose() {
      this.relevanceDialog = false;
    },
    handleRelevanceBtnClick() {
      this.relevanceDialog = false;
    },
    handleAssetDialogClose() {
      this.assetDialog = false;
      this.assetList = [{ name: '', quantity: '', type: '', link: '' }];
    },

    // w
    handthreeDialogClose() {
      this.threesDialog = false;
      this.threeList = [{ name: '', quantity: '', type: '', link: '', modelName: '' }];
    },

    addthreeRow() {
      this.threeList.push({
        name: '',
        type: '',
        quantity: '',
        link: '',
        modelName: '',
      });
    },

    openthreeLink(index) {
      this.linkthreeIndex = index; // 记录是哪一条
      this.threehao = this.threeList[index].link || ''; // 如果之前有值就显示
      this.ThreeDong = true;
    },

    handleThreeDialogLink() {
      if (this.linkthreeIndex !== null) {
        this.threeList[this.linkthreeIndex].link = this.threehao;
      }
      this.ThreeDong = false;
      this.threehao = '';
      this.currentIndex = null;
    },

    removeThreeRow(index) {
      this.threeList.splice(index, 1);
    },

    handlethreeDialogOpen() {
      for (let item of this.threeList) {
        let obj = {
          cadTreeId: this.treeLevelCurrentId,
          modelName: item.modelName,
          name: item.name,
          type: item.type,
          quantity: Number(item.quantity),
          link: item.link,
        };
        byProperty(obj).then(res => {
          if (res.success == true) {
            this.$message.success('新增成功');
            this.threeForm = {
              name: '',
              type: '',
              quantity: '',
              link: '',
              modelName: '',
            };
            this.threesDialog = false;
          } else {
          }
        });
      }
      this.threeList = [{ name: '', quantity: '', type: '', link: '', modelName: '' }];
    },
    // w

    handleAssetDialogOpen() {
      // setAssetMockData(this.assetList); // 保存 mock 数据
      for (let item of this.assetList) {
        let obj = {
          spaceId: this.currentClickSpaceData.id,
          cadTreeId: this.nodeData.id,
          name: item.name,
          type: item.type,
          quantity: Number(item.quantity),
          link: item.link,
        };
        spaceProperty(obj).then(res => {
          if (res.success == true) {
            this.$message.success('新增成功');
            this.assetForm = {
              name: '',
              type: '',
              quantity: '',
              link: '',
            };
            this.assetDialog = false;
          } else {
          }
        });
      }
      this.assetList = [{ name: '', quantity: '', type: '', link: '' }];
    },

    // w
    handleAssetDialogLook() {
      this.LookDong = false;
    },

    handleThreeDialogLook() {
      this.LookthhreeDong = false;
    },

    openThreeLook() {
      this.threesDialog = true;
      this.showMenu = false;
    },

    openZcLook() {
      this.LookthhreeDong = true;
      this.showMenu = false;
      const modelName = encodeURIComponent(this.selectedObject.name); // 确保正确编码
      cadProperty(modelName, this.treeLevelCurrentId)
        .then(res => {
          this.tablehreeData = res.data;
        })
        .catch(err => {});
    },

    loadPropertyData() {
      spaProperty(this.currentClickSpaceData.id, this.nodeData.id)
        .then(res => {
          this.tableData = res.data;
        })
        .catch(err => {});
    },

    copyToClipboard(text) {
      if (!text) {
        this.$message.warning('无链接可复制');
        return;
      }
      const input = document.createElement('input');
      input.value = text;
      document.body.appendChild(input);
      input.select();
      document.execCommand('copy');
      document.body.removeChild(input);
      this.$message.success('复制成功');
    },
    // w

    handleAssetDialogLink() {
      if (this.linkIndex !== null) {
        this.assetList[this.linkIndex].link = this.bianhao;
      }
      this.LinkDong = false;
      this.bianhao = '';
      this.currentIndex = null;
    },
    openLink(index) {
      this.linkIndex = index; // 记录是哪一条
      this.bianhao = this.assetList[index].link || ''; // 如果之前有值就显示
      this.LinkDong = true;
    },
    removeAssetRow(index) {
      this.assetList.splice(index, 1);
    },
    handleAssetBtnClick() {},
    handleNewCadDialogClose() {
      this.saveMapData.saveCADDialog = false;
    },
    handleNewCadBtnClick() {
      saveCAD({
        mapId: this.saveMapData.saveMapId,
        name: this.saveMapData.saveMapName,
        description: this.saveMapData.saveMapDes,
      }).then(res => {
        if (res.success) {
          this.$message.success('上传成功');
          this.saveMapData.saveMapId = '';
          this.saveMapData.saveMapName = '';
          this.saveMapData.saveMapDes = '';
        } else {
          this.$message.success('上传失败,请检查');
        }
        this.saveMapData.saveCADDialog = false;
      });
    },
    handleUpdateCadDialogClose() {
      this.updateMapData.updateCADDialog = false;
    },
    handleUpdateCadBtnClick() {
      updateCAD({
        id: this.updateMapData.updateId,
        name: this.updateMapData.updateMapName,
        description: this.updateMapData.updateMapDescribe,
      }).then(res => {
        console.log(res, '更新成功');
        if (res.success) {
          this.$message.success('更新成功');
        } else {
          this.$message.success('更新失败,请检查');
        }
        this.handleUpdateCadDialogClose();
      });
    },
    handleDeleteCadDialogClose() {
      this.deleteMapData.deleteCADDialog = false;
    },
    handleDeleteCadBtnClick() {
      deleteCADById(this.deleteMapData.deleteMapId).then(res => {
        console.log(res, '删除成功');
        if (res.success) {
          this.$message.success('删除成功');
        } else {
          this.$message.success('删除失败,请检查');
        }
        this.handleDeleteCadDialogClose();
      });
    },
    // 空间节点弹窗
    handleDeleteSpaceDialogClose() {
      this.deleteSpaceDialog = false;
    },
    // 删除空间节点
    handleDeleteSpaceBtnClick() {
      deleteSpaceById(this.currentClickSpaceData.id).then(res => {
        console.log(res, '删除空间ID成功');
        if (res.success) {
          this.$message.success('删除成功');
          this.handleDeleteSpaceDialogClose();
          const targetId = this.currentClickSpaceData.id;
          const source = this.map.getSource('label-source');
          if (!source) return;
          const rawData = source._data || {
            type: 'FeatureCollection',
            features: [],
          };
          const data = JSON.parse(JSON.stringify(rawData));

          // 过滤掉要删除的 feature
          const remainingFeatures = data.features.filter(
            feature => feature.properties.id != targetId
          );
          // 重新设置数据，触发图层更新
          source.setData({
            type: 'FeatureCollection',
            features: remainingFeatures,
          });
        } else {
          this.$message.success('删除失败,请检查');
        }
      });
    },
    handleCloseAddPanorama() {
      this.createPanorama.createShowVisiable = false;
      this.currentClickProperty = '';
    },
    // 全景图确定按钮
    handleAddPanorama() {
      // zyp  判断是否是3D界面的添加全景视图
      if (this.threeDYemian) {
        this.uploadCADPanoramaFormData = new FormData();
        this.uploadCADPanoramaFormData.set('name', this.createPanorama.createPanoramaName);
        this.uploadCADPanoramaFormData.set('description', '3D界面添加全景视图');
        this.uploadCADPanoramaFormData.set('iconIndex', '1');
        this.uploadCADPanoramaFormData.set('modelName', this.modelName);
        this.uploadCADPanoramaFormData.set('cadId', this.threePanoramaCadId);
        this.uploadCADPanoramaFormData.set('file', this.threePanoramaFile);
        savePanorama(this.uploadCADPanoramaFormData)
          .then(res => {
            console.log('上传成功:', res);
            if (res.success) {
              this.createPanorama.createShowVisiable = false;
              this.getPanoramaByCadId();
              this.$message.success('添加全景图成功');
            }
          })
          .catch(error => {
            console.error('上传失败:', error);
            this.$message.error('添加全景图失败');
          });
      } else {
        if (this.panoramaSign == 'edit') {
          this.uploadCADPanoramaFormData.set('id', this.panoramaCurrentID);
        }
        this.uploadCADPanoramaFormData.set('name', this.createPanorama.createPanoramaName);
        this.uploadCADPanoramaFormData.set('description', '');
        this.uploadCADPanoramaFormData.set('iconIndex', this.createPanorama.createIconIndex);
        if (this.panoramaSign == 'edit') {
          this.uploadCADPanoramaFormData.set('x', this.panoramaEditX);
          this.uploadCADPanoramaFormData.set('y', this.panoramaEditY);
        } else {
          const lngLat = this.eventMap.lngLat;
          this.uploadCADPanoramaFormData.set('x', lngLat.lng);
          this.uploadCADPanoramaFormData.set('y', lngLat.lat);
        }
        // this.eventMap
        console.log(this.createPanorama, 'ceshi');
        savePanorama(this.uploadCADPanoramaFormData)
          .then(res => {
            console.log('上传成功:', res);
            if (res.success) {
              this.createPanorama.createShowVisiable = false;
              this.createPanorama.createIconIndex = void 0;
              this.createPanorama.createPanoramaName = '';
              this.$refs.fileInput.value = '';
              // 先查询所有cad图再进行添加
              if (this.panoramaSign == 'edit') {
                getPanoramaByCadId(this.currentTreeNode.id).then(dynamicRes => {
                  console.log(dynamicRes);
                  if (dynamicRes.success) {
                    dynamicRes.data.forEach(item => {
                      if (item.id == this.panoramaCurrentID) {
                        let id = item.id;
                        let iconIndex = parseInt(item.iconIndex);
                        let img = this.createPanorama.createIconArray?.[iconIndex];
                        let name = item.name;
                        let url = item.url;
                        let graphicsFeatures = [];
                        if (img) {
                          const feature = {
                            type: 'Feature',
                            geometry: {
                              type: 'Point',
                              coordinates: [item.x, item.y],
                            },
                            properties: {
                              id,
                              iconIndex,
                              img,
                              url,
                              text: name,
                            },
                          };
                          graphicsFeatures.push(feature);
                          if (!this.map.hasImage(img)) {
                            const image = new Image();
                            image.crossOrigin = 'anonymous';
                            image.src = img;
                            image.onload = () => {
                              if (!this.map.hasImage(img)) {
                                this.map.addImage(img, image);
                              }
                            };
                          }
                        }
                        this.panoramaLayer.setData({
                          type: 'FeatureCollection',
                          features: graphicsFeatures,
                        });
                      }
                    });
                  }
                  console.log(this.panoramaCurrentID, '当前全景图id');
                });
              } else {
                getPanoramaByCadId(this.currentTreeNode.id).then(dynamicRes => {
                  let item = dynamicRes.data.at(-1);
                  let id = item.id;
                  let iconIndex = parseInt(item.iconIndex);
                  let img = this.createPanorama.createIconArray?.[iconIndex];
                  let name = item.name;
                  let url = item.url;
                  let graphicsFeatures = [];
                  if (img) {
                    const feature = {
                      type: 'Feature',
                      geometry: {
                        type: 'Point',
                        coordinates: [item.x, item.y],
                      },
                      properties: {
                        id,
                        iconIndex,
                        img,
                        url,
                        text: name,
                      },
                    };
                    graphicsFeatures.push(feature);
                    if (!this.map.hasImage(img)) {
                      const image = new Image();
                      image.crossOrigin = 'anonymous';
                      image.src = img;
                      image.onload = () => {
                        if (!this.map.hasImage(img)) {
                          this.map.addImage(img, image);
                        }
                      };
                    }
                  }
                  this.panoramaLayer.setData({
                    type: 'FeatureCollection',
                    features: graphicsFeatures,
                  });
                });
              }
            }
          })
          .catch(error => {
            console.error('上传失败:', error);
          });
      }
    },
    selectPanoramaIcon(index) {
      this.createPanorama.createIconIndex = index;
      console.log(this.createPanorama);
    },
    handleUploadPanorama() {
      this.uploadPanoramaClick();
    },
    handleCloseAddCamera() {
      this.createCamera.createShowVisiable = false;
      this.currentClickProperty = '';
    },
    // 添加摄像机确定按钮
    handleAddCamera() {
      if (this.createCameraSign == 'cad') {
        this.addVideo(
          this.createCamera.createIconIndex,
          this.createCamera.xAxis,
          this.createCamera.yAxis,
          this.createCamera.createCameraSerial
        );
      } else if (this.createCameraSign == 'panorama') {
        const videoIndex = this.createCamera.createIconIndex;
        const videoTips = this.createCamera.createCameraSerial;
        const videoPath = this.createCamera.createCameraPath;
        const data = {
          iconIndex: videoIndex,
          cadTreeId: this.treeLevelCurrentId,
          panoramaId: this.panoramaCurrentID,
          x: this.panoramaLongitude,
          y: this.panoramaLatitude,
          cameraNumber: videoTips,
          cameraUrl: videoPath,
        };
        console.log(data, '添加的全景图视频数据');
        postPanoramaAddVideo(data).then(res => {
          console.log(res, '全景图添加的视频');
          this.$message.success('监控视频添加成功!');
          getPanoramaAddVideo(this.treeLevelCurrentId, this.panoramaCurrentID).then(getRes => {
            const item = getRes.data.at(-1);
            const iconIndex = item.iconIndex;
            const iconTips = item.cameraNumber;
            const iconPlayUrl = item.cameraUrl;
            let addMarkers = {
              id: `video_${item.id}`,
              longitude: item.x,
              latitude: item.y,
              html: `
                  <div class="video-control-marker" data-play-url="${iconPlayUrl}" title="${iconTips}" data-id="${item.id}" style="position: relative;">
              <img src="${this.createCamera.createIconArray[iconIndex]}"
                  style="width: 18px; height: 18px" />
                    <span class="camera-marker-close" data-id="${item.id}">×</span>
            </div>
          `,
              anchor: 'center center',
              style: {},
              tooltip: '',
            };
            this.markersPlugin.addMarker(addMarkers);
            this.$nextTick(() => {
              const el = document.querySelector(`.video-control-marker[data-id="${item.id}"]`);
              const closeBtn = el?.querySelector('.camera-marker-close');
              if (el && closeBtn) {
                el.addEventListener('mouseenter', () => {
                  if (this.currentViewMode === 'editmode') {
                    closeBtn.style.display = 'block';
                  } else {
                    return;
                  }
                });
                el.addEventListener('mouseleave', () => {
                  closeBtn.style.display = 'none';
                });
              }
            });
          });
        });
      }
      this.createCamera.createShowVisiable = false;
    },
    selectCameraIcon(index) {
      this.createCamera.createIconIndex = index;
    },
    // 添加设备模板
    handleCloseAddDevice() {
      this.createDevice.createShowVisiable = false;
      this.currentNodeIsIcu = false;
      this.createDeviceShowCheck = false;
    },
    handleAddDevice() {
      // zyp3
      /* this.addDevice(
        this.createDevice.createIconIndex,
        this.createDevice.xAxis,
        this.createDevice.yAxis,
      ); */
      /* this.createDeviceShowCheck = false; */
      const lngLat = this.eventMap.lngLat;
      if (this.threeDYemian) {
        let obj = {
          iconIndex: 1, // 图标样式
          templateType: '1', // 模版的类型，前端定义
          x: '0',
          y: '0',
          modelName: this.modelName, // 如果是3D，传这个。CAD图不用传
          deviceId: this.threeDIcuData.id,
          cadTreeId: this.nodeData.id,
        };
        deviceTemplate(obj).then(res => {
          this.$message.success(res.data);
          this.selectThreeDTreeData();
        });
      } else {
        let obj = {
          iconIndex: this.createDevice.createIconIndex, // 图标样式
          templateType: '1', // 模版的类型，前端定义
          x: lngLat.lng,
          y: lngLat.lat,
          deviceId: this.threeDIcuData.id,
          cadTreeId: this.treeLevelCurrentId,
        };
        deviceTemplate(obj).then(res => {
          console.log(res, 'cad图上添加设备模板');
          this.$message.success(res.data);
          this.selectCadDeciceData();
        });
      }
      this.handleCloseAddDevice();
    },
    selectCadDeciceData() {
      selectDeviceTemplate(this.treeLevelCurrentId).then(res => {
        console.log(res, '查询cad图上的设备信息');
        this.deciceTimer && clearTimeout(this.deciceTimer);
        this.deciceTimer = setTimeout(() => {
          let graphicsFeatures = [];
          res.data.forEach(item => {
            let iconIndex = item.iconIndex;
            let img = this.createDevice.createIconArray[iconIndex];
            let id = item.id;
            let deviceObject = item;
            if (img) {
              const feature = {
                type: 'Feature',
                geometry: {
                  type: 'Point',
                  coordinates: [item.x, item.y],
                },
                properties: {
                  id,
                  iconIndex,
                  img,
                  deviceObject,
                },
              };
              graphicsFeatures.push(feature);
              if (!this.map.hasImage(img)) {
                const image = new Image();
                image.crossOrigin = 'anonymous';
                image.src = img;
                image.onload = () => {
                  if (!this.map.hasImage(img)) {
                    this.map.addImage(img, image);
                  }
                };
              }
            }
            this.deviceLayer.setData({
              type: 'FeatureCollection',
              features: graphicsFeatures,
            });
          });
        }, 500);
      });
    },
    deleteCadDevice(id) {
      deletedeviceTemplate(id).then(res => {
        console.log(res, '删除模板成功！！！');
        this.$message.success(res.data);
        this.selectCadDeciceData();
      });
    },
    selectDeviceIcon(index) {
      this.createDevice.createIconIndex = index;
    },
    handleAddDeviceChooseIcu() {
      // ICU 标识
      this.currentNodeIsIcu = true;
      this.createDeviceShowCheck = true;
      this.getDataTree();
    },
    handleAddDeviceTemplate() {
      this.deviceImageVisiable = true;
    },
    handleUpdateSpaceIDDialogClose() {
      this.updateSpaceData.updateSpaceIDDialog = false;
    },
    handleUpdateSpaceIDBtnClick(x = void 0, y = void 0) {
      console.log('移动后的xy坐标', x, y);
      updateSpace({
        id: this.currentClickSpaceData.id,
        name: this.updateSpaceData.updateSpaceName,
        description: this.updateSpaceData.updateSpaceDescribe,
        x,
        y,
        color: this.updateSpaceData.updateSpaceColor,
        size: this.updateSpaceData.updateSpaceFontSize,
        dataId: '',
        redisKey: '',
        mqttId: '',
      }).then(res => {
        if (res.success) {
          this.$message.success('更新成功');
          const targetId = this.currentClickSpaceData.id;
          const source = this.map.getSource('label-source');
          if (!source) return;

          const oldData = source._data || {
            type: 'FeatureCollection',
            features: [],
          };
          // 深拷贝防止原数据污染
          const newData = JSON.parse(JSON.stringify(oldData));

          // 更新对应feature的属性（局部更新）
          newData.features = newData.features.map(feature => {
            if (feature.properties.id == targetId) {
              // 更新属性
              return {
                ...feature,
                properties: {
                  ...feature.properties,
                  label: this.updateSpaceData.updateSpaceName,
                  color: this.updateSpaceData.updateSpaceColor,
                  size: parseInt(this.updateSpaceData.updateSpaceFontSize),
                  des: this.updateSpaceData.updateSpaceDescribe,
                },
                // 如果x,y发生了变化，可以更新geometry坐标，假设x,y是平面坐标，需要转换回经纬度
                geometry: {
                  ...feature.geometry,
                  coordinates:
                    x !== undefined && y !== undefined
                      ? this.map.toLngLat({ x, y }).toArray() // 这里假设你的map有toLngLat方法
                      : feature.geometry.coordinates,
                },
              };
            }
            return feature;
          });

          // 重新设置数据刷新图层
          source.setData(newData);

          this.handleUpdateSpaceIDDialogClose();
        } else {
          this.$message.success('删除失败,请检查');
        }
      });
    },
    async createQRcode() {
      if (this.triggerType == 'addThreeDModel') {
        try {
          let obj = {
            cadTreeId: this.nodeData.id,
            modelName: this.selectedObject.name,
          };
          const response = await createThreeDQRCode(obj);
          const blob = response.data;
          this.QRdata.QRcodeImg = URL.createObjectURL(blob);
          this.QRdata.QRcodeShow = true;
        } catch (error) {
          console.error('获取二维码失败', error);
        }
      } else {
        try {
          const response = await createQRcode(this.currentClickSpaceData.id);
          const blob = response.data;
          this.QRdata.QRcodeImg = URL.createObjectURL(blob);
          this.QRdata.QRcodeShow = true;
        } catch (error) {
          console.error('获取二维码失败', error);
        }
      }
    },
    handleBindMomeBtnClick() {
      createBindMome({
        id: this.currentClickSpaceData.id,
        mome: this.memoText,
      }).then(res => {
        console.log(res, '绑定空间id成功，备忘录');
        if (res.success) {
          this.addBookmarkDialog = false;
        }
      });
    },
    getMome() {
      this.memoText = '';
      getMome(this.currentClickSpaceData.id).then(res => {
        console.log(res, '查询的备忘录数据');
        if (res.success) {
          if (res.data) {
            this.memoText = res.data.mome;
          }
          console.log(this.memoText);
        }
      });
    },
    addChooseTextClick() {
      this.getDataTree();
    },
    // 获取数据目录树
    getDataTree() {
      this.treeDialog = true;
      getDataTree().then(res => {
        console.log(res, '获取数据目录树成功');
        res.data.children = res.data.children.filter((_, index) => index === 0);
        this.treeData = res.data.children;
        console.log(this.treeData);
      });
    },
    // 根据树目录id查询下层目录
    handleNodeClick(line) {
      console.log(line, 'line');
      this.threeDIcuData = line; // zyp3 保存设备模板3D模板目录
      if (line.type == 'device' && this.currentNodeIsIcu) {
        return;
      } else if (line.type == 'deviceGroup' || line.type == 'device') {
        getDataTreeById(line.id).then(res => {
          console.log(res);
          this.addChildrenToNode(this.treeData, line.id, Array.isArray(res.data) ? res.data : []);
          console.log(res, '根据树目录id查询下层目录成功');
        });
      } else if (line.type == 'subDevice') {
        getDataTreeById(line.id).then(res => {
          if (res.success) {
            console.log(res, '最终数据查询成功');
            this.treeLevelControlData = res.data;
            this.treeLevelControlVisiable = true;
          }
        });
      } else if (line.type == 'value_Analog') {
        this.histogramAddFrontData = { ...line };
      }
    },
    // 递归查找节点并插入子节点
    addChildrenToNode(nodes, targetId, childrenData) {
      console.log(childrenData);
      for (let node of nodes) {
        if (node.id === targetId) {
          if (!node.children) {
            this.$set(node, 'children', []);
          }
          node.children = [];
          node.children.push(...(Array.isArray(childrenData) ? childrenData : [])); // **追加新数据**
          this.$nextTick(() => {
            const treeNode = this.$refs.tree.getNode(targetId); // 获取 el-tree 节点对象
            if (treeNode) {
              treeNode.expand(); // **正确展开节点**
            } else {
              console.warn(`未找到 ID 为 ${targetId} 的节点`);
            }
          });
          return;
        }
        if (node.children) {
          this.addChildrenToNode(node.children, targetId, childrenData);
        }
      }
    },
    // 绑定数据点位接口
    handleBindPointSureClick() {
      console.log(this.treeLevelControlChooseData, this.triggerType);
      // 绑定数据接口方法
      if (this.triggerType == 'relatedData') {
        let linkParmas = { add: [], delete: [] };
        for (const item of this.treeLevelControlChooseData) {
          linkParmas.add.push({
            spaceId: this.currentClickSpaceData.id, // 空间id
            dataId: item.id, // 数据id
            redisKey: item.keyPrefix, // 数据前缀
            mqttId: item.mqttId,
          });
        }
        linkData(linkParmas).then(res => {
          console.log(res, '数据绑定成功!');
          if (res.success) {
            this.$message.success('数据绑定成功');
            this.treeDialog = false;
            this.treeLevelControlVisiable = false;
          }
        });
      } else if (this.triggerType == 'threeDModel') {
        let linkParmas = { add: [], delete: [] };
        for (const item of this.treeLevelControlChooseData) {
          linkParmas.add.push({
            cadTreeId: this.nodeData.id, // 空间id
            modelName: this.selectedObject.name,
            name: item.name,
            dataId: item.id, // 数据id
            redisKey: item.keyPrefix, // 数据前缀
            mqttId: item.mqttId,
          });
        }
        linkData(linkParmas).then(res => {
          console.log(res, '数据绑定成功!');
          if (res.success) {
            this.$message.success('数据绑定成功');
            this.treeDialog = false;
            this.treeLevelControlVisiable = false;
          }
        });
      } else if (this.triggerType == 'chooseData') {
        console.log('添加数据', this.treeLevelControlChooseData[0]);
        this.addCADDataForm.chooseTreeName = this.treeLevelControlChooseData[0].name;
        this.addCADDataForm.chooseTreeDataId = this.treeLevelControlChooseData[0].id;
        this.addCADDataForm.dataId = this.treeLevelControlChooseData[0].id;
        this.addCADDataForm.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
        this.addCADDataForm.mqttId = this.treeLevelControlChooseData[0].mqttId;
        this.treeDialog = false;
        this.treeLevelControlVisiable = false;

        console.log(this.addCADDataForm);
      } else if (this.triggerType == 'panoramaData') {
        console.log('调用树状平台添加的全景图数据', this.treeLevelControlChooseData);
        this.addCADDataForm.dataId = this.treeLevelControlChooseData[0].id;
        this.addCADDataForm.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
        this.addCADDataForm.mqttId = this.treeLevelControlChooseData[0].mqttId;
      } else if (this.triggerType == 'panoramaButton') {
        // 在此处获取全景图添加按钮的数据平台数据
        console.log(this.treeLevelControlChooseData[0], '数据平台数据');
        this.addPanoramaButtonData.chooseFrontDataName = this.treeLevelControlChooseData[0].name;
        this.addPanoramaButtonData.dataId = this.treeLevelControlChooseData[0].id;
        this.addPanoramaButtonData.mqttId = this.treeLevelControlChooseData[0].mqttId;
        this.addPanoramaButtonData.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
        this.treeLevelControlVisiable = false;
        this.treeDialog = false;
      } else if (this.triggerType == 'addPieChartData') {
        this.pieChatAddFrontData.push(this.treeLevelControlChooseData[0]);
        this.treeLevelControlVisiable = false;
        console.log(this.pieChatAddFrontData, '查询看板增添的front平台数据');
      } else if (this.triggerType == 'addLineChartData') {
        this.treeLevelControlChooseData.forEach(item => {
          this.lineChatAddFrontData.push(item);
        });
        this.treeLevelControlVisiable = false;
        console.log(this.lineChatAddFrontData, '查询看板增添的front折线图数据');
      } else if (this.triggerType == 'addThreeDModel') {
        if (this.ationMenu) {
          let linkParmas = { add: [], delete: [] };
          for (const item of this.treeLevelControlChooseData) {
            linkParmas.add.push({
              cadTreeId: this.nodeData.id, // 空间id
              modelName: this.selectedObject.name,
              name: item.name,
              dataId: item.id, // 数据id
              redisKey: item.keyPrefix, // 数据前缀
              mqttId: item.mqttId,
            });
          }
          linkData(linkParmas).then(res => {
            console.log(res, '数据绑定成功!');
            if (res.success) {
              this.$message.success('数据绑定成功');
              this.treeDialog = false;
              this.treeLevelControlVisiable = false;
            }
          });
        } else {
          console.log(this.treeLevelControlChooseData[0], '这个是3D绑定数据');
          this.addCADDataForm.chooseTreeName = this.treeLevelControlChooseData[0].name;
          this.addCADDataForm.chooseTreeDataId = this.treeLevelControlChooseData[0].id;
          this.addCADDataForm.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
          this.addCADDataForm.mqttId = this.treeLevelControlChooseData[0].mqttId;
          this.threeDBindHuiData.dataId = this.treeLevelControlChooseData[0].id;
          this.threeDBindHuiData.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
          this.threeDBindHuiData.mqttId = this.treeLevelControlChooseData[0].mqttId;
          this.treeLevelControlVisiable = false;
        }
      } else if (this.triggerType == 'boardAddData') {
        console.log('调用树状平台添加的看板数据', this.treeLevelControlChooseData);
        this.boardDataForm.chooseName = this.treeLevelControlChooseData[0].name;
        this.boardDataForm.dataId = this.treeLevelControlChooseData[0].id;
        this.boardDataForm.redisKey = this.treeLevelControlChooseData[0].keyPrefix;
        this.boardDataForm.mqttId = this.treeLevelControlChooseData[0].mqttId;
        this.treeLevelControlVisiable = false;
        this.treeDialog = false;
      }
    },
    handleTreeSureBtnClick() {
      if (this.currentShowDisplay == 'pie') {
        this.pieChartData = this.pieChatAddFrontData.map(item => {
          console.log(item);
          return {
            name: item.name,
            pointId: item.id,
            redisKey: item.keyPrefix,
            color: '#1890ff',
          };
        });
      } else if (this.currentShowDisplay == 'bar') {
        console.log(this.histogramAddFrontData, '点击的柱状图数据');
        this.barChartData.push({
          name: this.histogramAddFrontData.name,
          pointId: this.histogramAddFrontData.id,
          redisKey: this.histogramAddFrontData.keyPrefix,
          color: '#1890ff',
        });
      } else if (this.currentShowDisplay == 'line') {
        console.log(this.lineChatAddFrontData, '添加的数据');
        this.lineChartData = this.lineChatAddFrontData.map(item => {
          return {
            name: item.name,
            pointId: item.id,
            redisKey: item.keyPrefix,
            color: '#1890ff',
            lineBackgroundColor: '#1890ff',
          };
        });
      }
      this.treeDialog = false;
    },
    handlePieDataRowDelete(id) {
      const index = this.pieChartData.findIndex(item => item.id === id);
      if (index !== -1) {
        this.pieChartData.splice(index, 1);
      }
    },
    handleBarDataRowDelete(id) {
      const index = this.barChartData.findIndex(item => item.id === id);
      if (index !== -1) {
        this.barChartData.splice(index, 1);
      }
    },
    handleLineDataRowDelete(id) {
      const index = this.lineChartData.findIndex(item => item.id === id);
      if (index !== -1) {
        this.lineChartData.splice(index, 1);
      }
    },
    handleBindPonitChange(val) {
      this.treeLevelControlChooseData = val;
    },
    // 触发文件选择窗口
    uploadPanoramaClick() {
      console.log(this.$refs.fileInput, 'dayin');
      this.$refs.fileInput.click();
    },
    // 全景图片处理文件上传
    // 全景图片处理文件上传
    handleFileUpload(event) {
      const file = event.target.files[0];
      console.log(file);
      this.threePanoramaFile = file;
      this.threePanoramaCadId = this.currentTreeNode.id;
      this.uploadCADPanoramaFormData.append('file', file);
      this.uploadCADPanoramaFormData.append('cadId', this.currentTreeNode.id);
    },
    // 全景图提交
    handlePanoramaClick() {
      this.uploadCADPanoramaFormData.set('name', this.uploadCADPanoramaText);
      this.uploadCADPanoramaFormData.set('description', '');
      this.uploadCADPanoramaFormData.set('x', this.addPanorama.XAxis);
      this.uploadCADPanoramaFormData.set('y', this.addPanorama.YAxis);
      savePanorama(this.uploadCADPanoramaFormData)
        .then(res => {
          console.log('上传成功:', res);
          if (res.success) {
            this.uploadCADPanoramaDialog = false;
            this.panoramaImgPath = res.data;

            this.loadPanorama();
          }
        })
        .catch(error => {
          console.error('上传失败:', error);
        });
    },
    // 获取当前展示的全景图片
    getPanoramaByCadId() {
      getPanoramaByCadId(this.currentTreeNode.id).then(res => {
        console.log(res, '根据id获取全景图片');
        if (res.success) {
          // zyp
          if (this.threeDYemian) {
            this.panoramaArrayThree = res.data;
          } else {
            this.panoramaTimer && clearTimeout(this.panoramaTimer);
            this.panoramaTimer = setTimeout(() => {
              let graphicsFeatures = [];
              res.data.forEach(item => {
                let iconIndex = parseInt(item.iconIndex);
                let img = this.createPanorama.createIconArray?.[iconIndex];
                let name = item.name;
                let url = item.url;
                if (img) {
                  const feature = {
                    type: 'Feature',
                    geometry: {
                      type: 'Point',
                      coordinates: [item.x, item.y],
                    },
                    properties: {
                      id: item.id,
                      iconIndex,
                      img,
                      text: name,
                      url,
                    },
                  };
                  graphicsFeatures.push(feature);
                  if (!this.map.hasImage(img)) {
                    const image = new Image();
                    image.crossOrigin = 'anonymous';
                    image.src = img;
                    image.onload = () => {
                      if (!this.map.hasImage(img)) {
                        this.map.addImage(img, image);
                      }
                    };
                  }
                }
              });
              this.panoramaLayer.setData({
                type: 'FeatureCollection',
                features: graphicsFeatures,
              });
            }, 500);
          }
        }
      });
    },
    // 进首页查询所有逻辑组
    async getRightTree() {
      getRightTree().then(res => {
        console.log(res, '查询所有右侧逻辑组成功');
        this.treeLevelData = res.data;
        console.log(this.treeLevelData);
        // 默认显示的内容
        if (this.treeLocalStorageAddImageId) {
          this.checkTreeItem(this.treeLocalStorageAddImageId);
          return;
        }
        if (this.treeLocalStorageEditImageId) {
          this.checkTreeItem(this.treeLocalStorageEditImageId);
          return;
        }
        this.defaultUnfoldTreeLevelDataIndex = this.treeLevelData[0].id;
        if (!this.rightTreeMenuShowRefresh) {
          if (this.treeLevelData.length && this.treeLevelData[0].child.length) {
            const index = this.treeLevelData[0].child.findIndex(
              item => item.type === 'cad' || item.type === 'threeD' || item.type === 'image'
            );
            console.log(index);
            if (index !== -1) {
              const node = this.treeLevelData[0].child[index];
              this.nodeData = node; // zyp5
              setTimeout(() => {
                this.selectBtn();
              }, 2000);
              this.treeLevelCurrentId = this.treeLevelData[0].child[index].id;
              if (node.type == 'threeD') {
                this.nodeData.name = node.name;
                this.nodeData.id = node.id;

                this.fullOrfloor = true;
                this.loadCadModel(node.link);
              } else if (node.type == 'cad') {
                this.initMap(node.id, node.mapId);
              } else if (node.type == 'image') {
                this.treeLeveShowImage = node.link;
              }
              this.getDisplayByCadTree();
              this.getDisplayImage();
              this.getDisplayText();
            }
          }
        }
      });
    },
    // 查询图片地址
    checkTreeItem(id) {
      if (id) {
        const targetItem = this.findItemById(id, this.treeLevelData);
        if (targetItem) {
          console.log('找到了目标项:', targetItem);
          this.treeLeveShowImage = targetItem.link;
        } else {
          console.log('没有找到目标项');
        }
      }
    },
    findItemById(id, data) {
      for (const item of data) {
        if (item.id === id) {
          return item;
        }
        if (Array.isArray(item.child) && item.child.length > 0) {
          const found = this.findItemById(id, item.child);
          if (found) return found;
        }
      }
      return null;
    },
    renderContent(h, { node, data }) {
      const iconMap = {
        group: require('../assets/group/luoji.png'),
        threeD: require('../assets/group/3D.png'),
        cad: require('../assets/group/cad.png'),
        image: require('../assets/group/pictrue.png'),
        link: require('../assets/group/lianjie.png'),
      };
      const iconSrc = iconMap[data.type];

      const isSelected = data.id == this.treeLeveSelectedNodeId;

      return h(
        'span',
        {
          style: {
            display: 'flex',
            alignItems: 'center',
            borderRadius: '4px',
            padding: '2px 6px',
            cursor: 'pointer',
            userSelect: 'none',
          },
        },
        [
          h('img', {
            attrs: { src: iconSrc },
            style: {
              width: ['threeD', 'cad', 'image'].includes(data.type)
                ? '18px'
                : data.type === 'group'
                ? '17px'
                : '16px',
              height: ['threeD', 'cad', 'image'].includes(data.type)
                ? '18px'
                : data.type === 'group'
                ? '17px'
                : '16px',
              marginRight: '10px',
            },
          }),
          h(
            'span',
            {
              style: { color: isSelected ? 'white' : 'white', fontSize: '11px' }, // 你这里文字颜色一直是白，我保留了
            },
            data.name
          ),
        ]
      );
    },
    showPopover() {
      this.createGroupVisiable = true;
    },
    closeRootTree() {
      this.createGroupVisiable = false;
    },
    createRootTreeClick() {
      if (!this.createGroupInputValue) {
        this.$message.error('请填写逻辑组名称');
        return;
      }
      createRightTree({
        parentId: 0, // 如果是根⽬录，这⾥传0
        name: this.createGroupInputValue,
        type: 'group',
        link: '',
        mapId: '',
        description: '',
      }).then(res => {
        console.log(res, '添加逻辑组成功！');
        if (res.success) {
          this.$message.success('添加逻辑组成功');
          this.createGroupVisiable = false;
          this.createGroupInputValue = '';
          this.getRightTree();
        }
      });
    },
    handle3DModeClick(map = void 0) {
      this.fullOrfloor = true;
      this.loadCadModel(map);
    },
    getMenuImgStyle(sign) {
      if (['threeD', 'cad', 'image'].includes(sign)) {
        return { width: '18px', height: '18px', marginRight: '10px' };
      } else if (sign === 'group') {
        return { width: '17px', height: '17px', marginRight: '10px' };
      } else if (sign === 'edit' || sign == 'delete') {
        return { width: '19px', height: '19px', marginRight: '10px' };
      } else {
        return { width: '16px', height: '16px', marginRight: '10px' };
      }
    },
    getMenuItemStyle(sign) {
      if (['edit', 'delete'].includes(sign)) {
        return {
          marginLeft: '-2px',
        };
      }
    },
    groupContextMenuClick(item) {
      this.treeLocalStorageAddImageId = void 0;
      this.treeLocalStorageEditImageId = void 0;

      console.log(item);
      if (item.sign == 'delete') {
        this.treeDialogDeleteVisiable = true;
        return;
      } else if (item.sign == 'edit') {
        this.treeLevelDialogData.treeMenuText = '编辑';
        this.treeLevelDialogData.treeMenuType = this.currentTreeNode.type;
        this.treeLevelDialogData.treeDialogVisiable = true;

        console.log(this.currentTreeNode);
        this.treeLevelDialogData.editPutId = this.currentTreeNode.id;
        this.treeLevelDialogData.editPutParentID = this.currentTreeNode.parentId;
        if (this.currentTreeNode.type == 'group') {
          this.treeLevelDialogData.treeMenuGroup.text = this.currentTreeNode.name;
        } else if (this.currentTreeNode.type == 'threeD') {
          this.treeLevelDialogData.treeMenuThreeD.text = this.currentTreeNode.name;
          this.treeLevelDialogData.treeMenuThreeD.path = this.currentTreeNode.link;
        } else if (this.currentTreeNode.type == 'image') {
          this.treeLevelDialogData.treeMenuImage.text = this.currentTreeNode.name;
        } else if (this.currentTreeNode.type == 'cad') {
          this.treeLevelDialogData.treeMenuCad.text = this.currentTreeNode.name;
          this.treeLevelDialogData.treeMenuCad.mapID = this.currentTreeNode.mapId;
        } else if (this.currentTreeNode.type == 'link') {
          this.treeLevelDialogData.treeMenuLink.text = this.currentTreeNode.name;
          this.treeLevelDialogData.treeMenuLink.path = this.currentTreeNode.link;
        }
        console.log(this.currentTreeNode);
        console.log(this.treeLevelDialogData);
        return;
      }
      this.treeLevelDialogData.treeMenuText = item.context;
      this.treeLevelDialogData.treeMenuType = item.sign;
      this.treeLevelDialogData.treeDialogVisiable = true;
    },
    handle3dModelControlVisiable() {
      this.visible3dModelControlVisiable = !this.visible3dModelControlVisiable;
      this.showAngleView = !this.showAngleView;
    },
    handleRightTreeClose() {
      this.treeLevelDialogData.treeDialogVisiable = false;
    },
    // 打开文件选择器
    triggerFileSelect() {
      this.$refs.fileInput.click();
    },
    // 处理文件选择
    handleFileChange(event) {
      const file = event.target.files[0];
      if (!file) return;
      this.treeLevelDialogData.treeMenuImage.file = file;
    },
    handleRightMenuClick() {
      const _type = this.treeLevelDialogData.treeMenuType;
      if (_type == 'group') {
        const formData = new FormData();
        if (this.treeLevelDialogData.editPutId) {
          formData.set('id', this.treeLevelDialogData.editPutId);
        }
        if (this.treeLevelDialogData.editPutParentID) {
          formData.set('parentId', this.treeLevelDialogData.editPutParentID);
        } else {
          formData.set('parentId', this.currentTreeNode.id || 0);
        }
        formData.set('name', this.treeLevelDialogData.treeMenuGroup.text);
        formData.set('type', 'group');
        formData.set('link', ''); // 实际应由后端返回文件链接
        formData.set('mapId', '');
        formData.set('description', '');
        formData.forEach((value, key) => {
          console.log(key, value);
        });
        if (this.treeLevelDialogData.editPutId) {
          updateRightTree(formData).then(res => {
            console.log(res, '更新逻辑组成功！');
            if (res.success) {
              this.$message.success('更新逻辑组成功');
              this.treeLevelDialogData.treeMenuGroup.text = '';
              this.treeLevelDialogData.editPutId = '';
              this.treeLevelDialogData.editPutParentID = '';
              this.getRightTree();
            }
          });
        } else {
          createRightTree(formData).then(res => {
            console.log(res, '添加逻辑组成功！');
            if (res.success) {
              this.$message.success('添加逻辑组成功');
              this.treeLevelDialogData.treeMenuGroup.text = '';
              this.getRightTree();
            }
          });
        }
      } else if (_type == 'threeD') {
        // 不管是更新还是新增，都应该显示新增的内容，所以不用默认加载
        this.rightTreeMenuShowRefresh = true;
        const formData = new FormData();
        if (this.treeLevelDialogData.editPutId) {
          formData.set('id', this.treeLevelDialogData.editPutId);
        }
        if (this.treeLevelDialogData.editPutParentID) {
          formData.set('parentId', this.treeLevelDialogData.editPutParentID);
        } else {
          formData.set('parentId', this.currentTreeNode.id || 0);
        }
        formData.set('name', this.treeLevelDialogData.treeMenuThreeD.text);
        formData.set('type', 'threeD');
        formData.set('link', this.treeLevelDialogData.treeMenuThreeD.path); // 实际应由后端返回文件链接
        formData.set('mapId', '');
        formData.set('description', '');
        if (this.treeLevelDialogData.editPutId) {
          updateRightTree(formData).then(res => {
            console.log(res, '更新3D模型成功！');
            if (res.success) {
              this.$message.success('更新3D模型成功');
              this.getRightTree();
              this.fullOrfloor = true;
              this.loadCadModel(this.treeLevelDialogData.treeMenuThreeD.path);
              this.treeLevelDialogData.treeMenuThreeD.text = '';
              this.treeLevelDialogData.treeMenuThreeD.path = '';
              this.treeLevelDialogData.editPutId = '';
              this.treeLevelDialogData.editPutParentID = '';
            }
          });
        } else {
          createRightTree(formData).then(res => {
            console.log(res, '添加3D模型成功！');
            if (res.success) {
              this.$message.success('添加3D模型成功');
              this.getRightTree();
              this.fullOrfloor = true;
              this.loadCadModel(this.treeLevelDialogData.treeMenuThreeD.path);
              this.treeLevelDialogData.treeMenuThreeD.text = '';
              this.treeLevelDialogData.treeMenuThreeD.path = '';
            }
          });
        }
      } else if (_type == 'image') {
        const { text, file } = this.treeLevelDialogData.treeMenuImage;
        const formData = new FormData();
        if (this.treeLevelDialogData.editPutId) {
          formData.set('id', this.treeLevelDialogData.editPutId);
        }
        if (this.treeLevelDialogData.editPutParentID) {
          formData.set('parentId', this.treeLevelDialogData.editPutParentID);
        } else {
          formData.set('parentId', this.currentTreeNode.id || 0);
        }

        formData.set('name', text);
        formData.set('file', file);
        formData.set('type', 'image');
        formData.set('link', ''); // 实际应由后端返回文件链接
        formData.set('mapId', '');
        formData.set('description', '');
        if (this.treeLevelDialogData.editPutId) {
          updateRightTree(formData).then(res => {
            console.log(res, '更新图片成功');
            if (res.success) {
              console.log(res);
              this.$message.success('更新图片成功');
              // 如果是新增图片，先记录一下id号
              this.treeLocalStorageEditImageId = this.treeLevelDialogData.editPutId;
              this.treeLevelDialogData.treeMenuImage.text = '';
              this.treeLevelDialogData.treeMenuImage.file = null;
              // this.treeLevelDialogData.editPutId = "";
              // this.treeLevelDialogData.editPutParentID = "";
              this.getRightTree();
            }
          });
        } else {
          createRightTree(formData).then(res => {
            console.log(res, '添加图片成功！');
            if (res.success) {
              this.$message.success('添加图片成功');
              // 如果是新增图片，先记录一下id号
              this.treeLocalStorageAddImageId = res.data;
              this.treeLevelDialogData.treeMenuImage.text = '';
              this.treeLevelDialogData.treeMenuImage.file = null;
              this.getRightTree();
            }
          });
        }
      } else if (_type == 'cad') {
        // 不管是更新还是新增，都应该显示新增的内容，所以不用默认加载
        this.rightTreeMenuShowRefresh = true;
        const formData = new FormData();
        if (this.treeLevelDialogData.editPutId) {
          formData.set('id', this.treeLevelDialogData.editPutId);
        }
        if (this.treeLevelDialogData.editPutParentID) {
          formData.set('parentId', this.treeLevelDialogData.editPutParentID);
        } else {
          formData.set('parentId', this.currentTreeNode.id || 0);
        }
        formData.set('name', this.treeLevelDialogData.treeMenuCad.text);
        formData.set('type', 'cad');
        formData.set('link', '');
        formData.set('mapId', this.treeLevelDialogData.treeMenuCad.mapID);
        formData.set('description', '');
        if (this.treeLevelDialogData.editPutId) {
          updateRightTree(formData).then(res => {
            console.log(res, '更新CAD成功！');
            if (res.success) {
              this.$message.success('更新CAD成功');
              this.initMap(void 0, this.treeLevelDialogData.treeMenuCad.mapID);
              this.treeLevelDialogData.treeMenuCad.text = '';
              this.treeLevelDialogData.treeMenuCad.mapID = '';
              this.treeLevelDialogData.editPutId = '';
              this.treeLevelDialogData.editPutParentID = '';
              this.getRightTree();
            }
          });
        } else {
          createRightTree(formData).then(res => {
            console.log(res, '添加CAD成功！');
            if (res.success) {
              this.$message.success('添加CAD成功');
              this.initMap(void 0, this.treeLevelDialogData.treeMenuCad.mapID);
              this.treeLevelDialogData.treeMenuCad.text = '';
              this.treeLevelDialogData.treeMenuCad.mapID = '';
              this.getRightTree();
            }
          });
        }
      } else if (_type == 'link') {
        const formData = new FormData();
        if (this.treeLevelDialogData.editPutId) {
          formData.set('id', this.treeLevelDialogData.editPutId);
        }
        if (this.treeLevelDialogData.editPutParentID) {
          formData.set('parentId', this.treeLevelDialogData.editPutParentID);
        } else {
          formData.set('parentId', this.currentTreeNode.id || 0);
        }
        formData.set('name', this.treeLevelDialogData.treeMenuLink.text);
        formData.set('type', 'link');
        formData.set('link', this.treeLevelDialogData.treeMenuLink.path); // 实际应由后端返回文件链接
        formData.set('mapId', '');
        formData.set('description', '');
        if (this.treeLevelDialogData.editPutId) {
          updateRightTree(formData).then(res => {
            console.log(res, '更新链接成功！');
            if (res.success) {
              this.$message.success('更新链接成功');
              ipcRenderer.send('open-url', this.treeLevelDialogData.treeMenuLink.path);
              this.treeLevelDialogData.treeMenuLink.text = '';
              this.treeLevelDialogData.treeMenuLink.path = '';
              this.treeLevelDialogData.editPutId = '';
              this.treeLevelDialogData.editPutParentID = '';
              this.getRightTree();
            }
          });
        } else {
          createRightTree(formData).then(res => {
            console.log(res, '添加链接成功！');
            if (res.success) {
              this.$message.success('添加链接成功');
              ipcRenderer.send('open-url', this.treeLevelDialogData.treeMenuLink.path);
              this.treeLevelDialogData.treeMenuLink.text = '';
              this.treeLevelDialogData.treeMenuLink.path = '';
              this.getRightTree();
            }
          });
        }
      }
      this.treeLevelDialogData.treeDialogVisiable = false;
    },
    handleRightNodeClick(data, node, component) {
      const tree = this.$refs.levelTree;
      if (!tree) return;
      const nodeInstance = tree.getNode(data);
      if (nodeInstance && nodeInstance.childNodes && nodeInstance.childNodes.length > 0) {
        nodeInstance.expanded = !nodeInstance.expanded;
      }
      this.handleExtendRightNodeClick?.(data, node, component);
    },
    handleExtendRightNodeClick(node) {
      clearInterval(this.timer);
      this.nodeData = node;
      this.treeLocalStorageAddImageId = void 0;
      this.treeLocalStorageEditImageId = void 0;
      console.log(node);
      this.rightTreeMenuShowRefresh = false;
      this.currentTreeNode = node;
      this.clickCurrentFloor = node.name;
      this.treeLevelCurrentId = node.id;
      this.treeLeveSelectedNodeId = node.id;
      this.showMenu = false;
      // 查询设备模板信息 zyp3
      this.selectThreeDTreeData();
      if (node.type == 'threeD') {
        this.threeDYemian = true; // zyp
        this.treeLeveShowImage = void 0;
        this.fullOrfloor = true;
        this.loadCadModel(node.link);
        this.showAngleView = true;
        this.getPanoramaByCadId(); // zyp
        this.$nextTick(() => {
          this.$refs.cameraPanel.viewData(node);
        });
      } else if (node.type == 'cad') {
        this.threeDYemian = false; // zyp
        this.treeLeveShowImage = void 0;
        this.initMap(node.id, node.mapId); // 记录一下后续刷新页面使用
        this.recordRightTreeNodeId = node.id;
        this.recordRightTreeMapId = node.mapId;
        this.getPanoramaByCadId();
        this.showAngleView = false;
      } else if (node.type == 'link') {
        this.threeDYemian = false; // zyp
        ipcRenderer.send('open-url', node.link);
        this.showAngleView = false;
      } else if (node.type == 'image') {
        this.threeDYemian = false; // zyp
        if (this.map && !this.isMapLoading) {
          this.map.remove();
          this.map = void 0;
          vjmap.clearPrewarmedResources();
        }
        this.clearModel();
        this.fullOrfloor = false;
        this.showAngleView = false;
        this.treeLeveShowImage = node.link;
      } else {
        return;
      }
      this.getDisplayByCadTree();
      this.getDisplayImage();
      this.getDisplayText();
      this.selectBtn();
      // this.selectCadDeciceData();
    },
    handleRightMenuShowClick(event, data, node, component) {
      event.preventDefault();
      event.stopPropagation();
      this.currentTreeNode = data;
      if (data.type === 'group') {
        this.groupContextMenu = [
          {
            context: '添加逻辑组',
            sign: 'group',
            img: require('../assets/group/luoji.png'),
          },
          {
            context: '添加3D页面',
            sign: 'threeD',
            img: require('../assets/group/3D.png'),
          },
          {
            context: '添加CAD页面',
            sign: 'cad',
            img: require('../assets/group/cad.png'),
          },
          {
            context: '添加图片页面',
            sign: 'image',
            img: require('../assets/group/pictrue.png'),
          },
          {
            context: '添加链接',
            sign: 'link',
            img: require('../assets/group/lianjie.png'),
          },
          {
            context: '编辑',
            sign: 'edit',
            img: require('../assets/group/edit.png'),
          },
          {
            context: '删除',
            sign: 'delete',
            img: require('../assets/group/delete.png'),
          },
        ];
      } else {
        this.groupContextMenu = [
          {
            context: '编辑',
            sign: 'edit',
            img: require('../assets/group/edit.png'),
          },
          {
            context: '删除',
            sign: 'delete',
            img: require('../assets/group/delete.png'),
          },
        ];
      }
      const menuWidth = 150;
      const menuHeight = this.groupContextMenu.length * 32;
      const pageWidth = window.innerWidth;
      const pageHeight = window.innerHeight;

      let left = event.clientX + 30;
      let top = event.clientY;

      if (left + menuWidth > pageWidth) left = pageWidth - menuWidth;
      if (top + menuHeight > pageHeight) top = pageHeight - menuHeight;

      this.menuX = left;
      this.menuY = top;
      this.menuVisible = true;

      clearTimeout(this.hideMenuTimer);
      this.hideMenuTimer = setTimeout(() => {
        this.menuVisible = false;
      }, 2000);

      document.addEventListener('click', this.handleOutsideClick);
    },
    handleMouseEnter() {
      clearTimeout(this.hideMenuTimer); // 鼠标进入菜单，暂停隐藏
    },
    handleMouseLeave() {
      // 鼠标离开菜单，再开始倒计时
      this.hideMenuTimer = setTimeout(() => {
        this.menuVisible = false;
      }, 2000);
    },
    handleOutsideClick() {
      this.menuVisible = false;
      clearTimeout(this.hideMenuTimer);
      document.removeEventListener('click', this.handleOutsideClick);
    },
    handleDeleteTreeDialogClose() {
      this.treeDialogDeleteVisiable = false;
    },
    handleDeleteTreeBtnClick() {
      console.log(this.currentTreeNode);
      deleteRightTree(this.currentTreeNode.id).then(res => {
        console.log(res);
        if (res.success) {
          this.$message.success('删除成功');
          this.getRightTree();
          this.treeDialogDeleteVisiable = false;
        }
      });
    },
    handleCameraViewClose() {
      this.dialogVisible = false;
      // this.three_monitoring = false;
      // this.$nextTick(() => {
      //   const webview = this.$refs.cameraView;
      //   if (webview && webview.remove) {
      //     webview.remove();
      //   }
      // });
    },
    handleCameraViewClose2() {
      this.dialogVisible2 = false;
    },
    handleDeviceImageVisiable() {
      this.deviceImageVisiable = false;
    },
    handleChooseDeviceTemplateClick() {
      this.deviceImageVisiable = false;
    },
    handlePanoramaMomeClick() {
      this.memoPanoramaText = '';
      getMomeInPanorama(this.panoramaCurrentID).then(res => {
        console.log(res, '查询的全景图的备忘录数据');
        if (res.success) {
          if (res.data) {
            this.memoPanoramaText = res.data.mome;
          }
          console.log(this.memoText);
        }
      });
    },
    handlePanoramaGetMomeClick() {
      this.panoramaNameShowMome = this.panoramaName + ' - 备忘录';
      this.addBookmarkPanoramaDialog = true;
    },
    handleBindMomePanoramaBtnClick() {
      createBindMomeInPanorama({
        id: this.panoramaCurrentID,
        mome: this.memoPanoramaText,
      }).then(res => {
        console.log(res, '绑定空间id成功，全景图备忘录');
        if (res.success) {
          this.addBookmarkPanoramaDialog = false;
        }
      });
    },
    handleBookmarkPanoramaClose() {
      this.addBookmarkPanoramaDialog = false;
    },
    selectDeviceImage(index) {
      this.createDeviceSelectedImageIndex = index;
    },
    // 能耗代码 - - -- start
    // 关闭弹窗
    // 页面加载的时候查询饼状图数据
    getDisplayByCadTree() {
      // 每次调用时清除之前的定时器
      // if (this.autoChartsRefreshTimer) {
      //   clearInterval(this.autoChartsRefreshTimer);
      //   this.autoChartsRefreshTimer = null;
      // }

      // 调用实际接口
      getDisplayByCadTree(this.treeLevelCurrentId).then(res => {
        console.log(res, '获取到echats数据');
        res.data.forEach(item => {
          if (item.type === 'pie') {
            this.customPieFloor = item.name;
          } else if (item.type === 'histogram') {
            this.customBarFloor = item.name;
          } else if (item.type === 'line') {
            this.customLineFloor = item.name;
          }
        });

        this.charts = res.data.map(item => {
          return {
            id: item.id,
            cadTreeId: item.cadTreeId,
            graphics: item.graphics,
            name: item.name,
            nameColor: item.nameColor,
            type: item.type,
            data:
              item.graphics.piePoints ||
              item.graphics.histogramPoints ||
              item.graphics.linePoints ||
              [],
            times: item.graphics.times || [],
            x: parseInt(item.x),
            y: parseInt(item.y),
            width: Math.abs(item.width),
            height: Math.abs(item.height),
            backgroundColor: item.backgroundColor,
          };
        });
        console.log(this.charts);
        this.$nextTick(() => {
          // 销毁旧图表
          Object.keys(this.chartInstances).forEach(id => {
            if (this.chartInstances[id]) {
              this.chartInstances[id].dispose();
            }
          });
          this.chartInstances = {};

          // 初始化新图表
          this.charts.forEach(chart => {
            this.initChart(chart.id, chart.type);
          });
        });

        // 设置新的自动刷新定时器（5分钟）
        // this.autoChartsRefreshTimer = setInterval(() => {
        //   this.getDisplayByCadTree();
        // }, 5 * 60 * 1000); // 5分钟
      });
    },
    handleClose() {
      this.energyModel = false;
    },
    handleClosePie() {
      this.energyModelPie = false;
    },
    handleCloseLine() {
      this.energyModelLine = false;
    },
    handleCloseText() {
      this.boardTextForm.visiable = false;
    },
    handleCloseUpload() {
      this.uploadDialogVisible = false;
    },
    // 上传背景图
    handleUpload(file) {
      if (!file.type.startsWith('image/')) {
        this.$message.error('只能上传图片！');
        return false;
      }

      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        this.backgroundImage = reader.result;
        this.uploadDialogVisible = false;
        this.$message.success('背景图片上传成功！');
      };
      return false; // 阻止自动上传
    },
    // 添加echarts图
    addChart(type) {
      this.currentDisplaySign = 'add';
      console.log(type);
      if (type === 'pie') {
        this.energyModelPie = true;
      }
      if (type == 'bar') {
        this.energyModel = true;
      }
      if (type == 'line') {
        this.energyModelLine = true;
      }
      console.log(this.charts);
    },
    // 饼状图确定
    confirmPieChart() {
      console.log(this.charts);

      const data = {
        type: 'pie',
        name: this.customPiePostFloor,
        nameColor: this.customPiePostNameBackgroundColor,
        cadTreeId: this.treeLevelCurrentId,
        x: this.customPiePostX,
        y: this.customPiePostY,
        width: this.customPiePostWidth,
        height: this.customPiePostHeight,
        backgroundColor: this.customPiePostBackgroundColor,
        content: {
          piePoints: this.pieChartData,
        },
        ...(this.currentDisplaySign == 'edit' && { id: this.currentDisplayEditId }),
      };
      console.log(data, '查询添加的饼状图数据');
      // return;
      createDisplay(data).then(res => {
        console.log(res, '饼状图添加成功');
        if (res.success) {
          this.$message.success('饼状图添加成功');
          this.getDisplayByCadTree();
          this.pieChartData = [];
          this.pieChatAddFrontData = [];
          this.customPieFloor = '';
          this.customPiePostFloor = '';

          this.customPiePostWidth = 200;
          this.customPiePostHeight = 200;
          this.customPiePostX = 0;
          this.customPiePostY = 0;
          this.customPiePostBackgroundColor = '';
          this.customPiePostNameBackgroundColor = '#ccc';
        }
        this.energyModelPie = false;
      });
    },
    // 柱状图确认
    confirmBarChart() {
      const hours = parseInt(this.barChartHours);
      if (isNaN(hours) || hours < 1 || hours > 720) {
        this.$message.warning('请输入1到720之间的小时数');
        return;
      }
      const data = {
        type: 'histogram',
        name: this.customBarPostFloor,
        nameColor: this.customBarPostNameBackgroundColor,
        cadTreeId: this.treeLevelCurrentId,
        x: this.customBarPostX,
        y: this.customBarPostY,
        width: this.customBarPostWidth,
        height: this.customBarPostHeight,
        backgroundColor: this.customBarPostBackgroundColor,
        content: {
          hours: parseInt(this.barChartHours),
          yMax: this.yAxisMaxBar,
          yMin: this.yAxisMinBar,
          unit: this.yAxisUnitBar,
          histogramPoints: this.barChartData,
        },
        ...(this.currentDisplaySign == 'edit' && { id: this.currentDisplayEditId }),
      };
      console.log(data, '查询添加的柱状图数据');
      createDisplay(data).then(res => {
        console.log(res, '柱状图添加成功');
        if (res.success) {
          this.$message.success('柱状图添加成功');
          this.getDisplayByCadTree();
          this.barChartData = [];
          this.customBarFloor = '';
          this.customBarPostFloor = '';
          this.barChartHours = void 0;

          this.customBarPostWidth = 200;
          this.customBarPostHeight = 200;
          this.customBarPostX = 0;
          this.customBarPostY = 0;
          this.customBarPostBackgroundColor = '';
          this.customBarPostNameBackgroundColor = '#ccc';
        }
        this.energyModel = false;
      });
    },
    // 曲线图确认
    confirmLineChart() {
      const hours = parseInt(this.lineChartHours);
      if (isNaN(hours) || hours < 1 || hours > 168) {
        this.$message.warning('请输入1到168之间的小时数');
        return;
      }
      const data = {
        type: 'line',
        name: this.customLinePostFloor,
        nameColor: this.customLinePostNameBackgroundColor,
        cadTreeId: this.treeLevelCurrentId,
        x: this.customLinePostX,
        y: this.customLinePostY,
        width: this.customLinePostWidth,
        height: this.customLinePostHeight,
        backgroundColor: this.customLinePostBackgroundColor,
        content: {
          lineType: this.radio,
          hours: parseInt(this.lineChartHours),
          yMax: this.yAxisMaxLine,
          yMin: this.yAxisMinLine,
          unit: this.yAxisUnitLine,
          linePoints: this.lineChartData,
        },
        ...(this.currentDisplaySign == 'edit' && { id: this.currentDisplayEditId }),
      };
      console.log(data, '查询添加的折线图数据');
      createDisplay(data).then(res => {
        console.log(res, '折线图添加成功');
        if (res.success) {
          this.$message.success('折线图添加成功');
          this.getDisplayByCadTree();
          this.lineChartData = [];
          this.lineChatAddFrontData = [];
          this.customLineFloor = '';
          this.customLinePostFloor = '';
          this.lineChartHours = void 0;

          this.customLinePostWidth = 200;
          this.customLinePostHeight = 200;
          this.customLinePostX = 0;
          this.customLinePostY = 0;
          this.customLinePostBackgroundColor = '';
          this.customLinePostNameBackgroundColor = '#ccc';
        }
        this.energyModelLine = false;
      });
    },
    // 饼状图添加一行新数据
    addPieChartData() {
      this.createDeviceShowCheck = true;
      this.getDataTree();
      this.triggerType = 'addPieChartData';
    },
    // 柱状图添加一行新数据
    addBarChartData() {
      this.createDeviceShowCheck = true;
      this.getDataTree();
      this.triggerType = 'addBarChartData';
    },
    // 曲线图添加一行新数据
    addLineChartData() {
      this.createDeviceShowCheck = true;
      this.getDataTree();
      this.triggerType = 'addLineChartData';
    },
    // 饼状图删除一行数据
    removePieChartData(index) {
      this.pieChartData.splice(index, 1);
    },
    // 柱状图删除一行数据
    removeBarChartData() {
      this.barChartData.splice(index, 1);
    },
    // 曲线图删除一行数据
    removeLineChartData() {
      this.lineChartData.splice(index, 1);
    },
    // 查看文本
    getDisplayText() {
      getDisplayText(this.treeLevelCurrentId).then(res => {
        console.log(res, '查看看板文本数据');
        if (res.success) {
          this.displayTextArray = res.data;
        }
      });
    },
    // 添加文本
    addText() {
      console.log(this.boardTextForm);
      const data = {
        id: this.displayTextStatus == 'add' ? '' : this.displayTextEditId,
        text: this.boardTextForm.text,
        link: this.boardTextForm.link,
        size: this.boardTextForm.size,
        color: this.boardTextForm.color,
        x: this.boardTextForm.x,
        y: this.boardTextForm.y,
        cadTreeId: this.treeLevelCurrentId,
        fontWeight: this.boardTextForm.bold,
      };
      console.log(data);
      postDisplayText(data).then(res => {
        console.log(res, '看板添加文本数据成功');
        if (res.success) {
          this.$message.success('文本添加成功');
          this.getDisplayText();
        }
      });
      this.boardTextForm.visiable = false;
    },
    // 编辑echarts配置
    editChart(id) {
      this.currentDisplaySign = 'edit';
      this.currentDisplayEditId = id;

      const chart = this.charts.find(c => c.id == id);
      if (chart.type == 'pie') {
        this.currentShowDisplay = 'pie';
        this.energyModelPie = true;
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.piePoints.forEach(item => {
          delete item.values;
        });
        this.customPiePostFloor = chart.name;
        this.customPiePostBackgroundColor = chart.backgroundColor;
        this.customPiePostNameBackgroundColor = chart.nameColor;
        this.customPiePostX = chart.x;
        this.customPiePostY = chart.y;
        this.customPiePostWidth = chart.width;
        this.customPiePostHeight = chart.height;
        this.pieChartData = chart.graphics.piePoints;
      } else if (chart.type == 'histogram') {
        this.currentShowDisplay = 'bar';
        this.energyModel = true;
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.histogramPoints.forEach(item => {
          delete item.values;
        });
        this.customBarPostFloor = chart.name;
        this.customBarPostBackgroundColor = chart.backgroundColor;
        this.customBarPostNameBackgroundColor = chart.nameColor;
        this.yAxisMaxBar = chart.graphics.yMax;
        this.yAxisMinBar = chart.graphics.yMin;
        this.yAxisUnitBar = chart.graphics.unit;
        this.barChartHours = chart.graphics.hours;
        this.customBarPostX = chart.x;
        this.customBarPostY = chart.y;
        this.customBarPostWidth = chart.width;
        this.customBarPostHeight = chart.height;
        this.barChartData = chart.graphics.histogramPoints;
      } else if (chart.type == 'line') {
        this.currentShowDisplay = 'line';
        this.energyModelLine = true;
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.linePoints.forEach(item => {
          delete item.values;
        });
        this.customLinePostFloor = chart.name;
        this.customLinePostBackgroundColor = chart.backgroundColor;
        this.customLinePostNameBackgroundColor = chart.nameColor;
        this.radio = chart.graphics.lineType;
        this.yAxisMaxLine = chart.graphics.yMax;
        this.yAxisMinLine = chart.graphics.yMin;
        this.yAxisUnitLine = chart.graphics.unit;
        this.lineChartHours = chart.graphics.hours;
        this.customLinePostX = chart.x;
        this.customLinePostY = chart.y;
        this.customLinePostWidth = chart.width;
        this.customLinePostHeight = chart.height;
        this.lineChartData = chart.graphics.linePoints;
      }
    },
    // 保存echarts配置
    saveChart(id) {
      const chart = this.charts.find(c => c.id == id);
      console.log(chart);
      if (chart.width < 0 || chart.height < 0) {
        console.error(`图表尺寸非法，width=${chart.width}，height=${chart.height}`);
        this.$message.error('图表宽高不能为负数，请检查后再提交');
        return;
      }
      let data = {};
      if (chart.type == 'pie') {
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.piePoints.forEach(item => {
          delete item.values;
        });
        data = {
          id: chart.id,
          name: chart.name,
          type: chart.type,
          x: chart.x,
          y: chart.y,
          width: chart.width,
          height: chart.height,
          backgroundColor: chart.backgroundColor,
          cadTreeId: chart.cadTreeId,
          content: chart.graphics,
        };
        console.log(data, '需要提交修改的柱状图数据');
        createDisplay(data).then(res => {
          console.log(res, '修改柱状图数据');
          if (res.success) {
            this.$message.success('修改成功!');
          }
        });
      } else if (chart.type == 'histogram') {
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.histogramPoints.forEach(item => {
          delete item.values;
        });
        data = {
          id: chart.id,
          name: chart.name,
          type: chart.type,
          x: chart.x,
          y: chart.y,
          width: chart.width,
          height: chart.height,
          backgroundColor: chart.backgroundColor,
          cadTreeId: chart.cadTreeId,
          content: chart.graphics,
        };
        console.log(data, '需要提交修改的折线图数据');
        createDisplay(data).then(res => {
          console.log(res, '修改折线图数据');
          if (res.success) {
            this.$message.success('修改成功!');
          }
        });
      } else if (chart.type == 'line') {
        delete chart.times;
        delete chart.graphics.times;
        chart.graphics.linePoints.forEach(item => {
          delete item.values;
        });
        data = {
          id: chart.id,
          name: chart.name,
          type: chart.type,
          x: chart.x,
          y: chart.y,
          width: chart.width,
          height: chart.height,
          backgroundColor: chart.backgroundColor,
          cadTreeId: chart.cadTreeId,
          content: chart.graphics,
        };
        console.log(data, '需要提交修改的折线图数据');
        createDisplay(data).then(res => {
          console.log(res, '修改折线图数据');
          if (res.success) {
            this.$message.success('修改成功!');
          }
        });
      }
    },
    // 移除echarts图
    removeChart(id) {
      console.log(id);
      deleteDisplay(id).then(res => {
        console.log(res, '看板删除成功');
        this.$message.success('看板删除成功');
        this.getDisplayByCadTree();
      });
    },
    // 移除文本
    removeText(id) {
      this.texts = this.texts.filter(text => text.id !== id);
    },
    // 保存添加新图
    setChartRef(el, id) {
      if (el) {
        this.$nextTick(() => this.initChart(id, this.charts.find(chart => chart.id === id)?.type));
      }
    },
    // 初始化echarts图
    initChart(id, type) {
      this.$nextTick(() => {
        const chartIndex = this.charts.findIndex(chart => chart.id === id);
        if (chartIndex !== -1) {
          const container = this.$el.querySelectorAll('.chart-box')[chartIndex];
          if (container && !this.chartInstances[id]) {
            this.chartInstances[id] = echarts.init(container);
            let option = {};
            if (type === 'histogram') {
              const chart = this.charts.find(chart => chart.id === id);
              const values = chart.data[0].values.map(item => item[1]);
              const yMin = 0;
              const yMax = Math.max(...values);
              console.log('柱状图的chart', chart);
              option = {
                backgroundColor: chart.backgroundColor,
                grid: {
                  left: '0%',
                  containLabel: true,
                },
                title: {
                  text: chart.name,
                  left: 'left',
                  top: '5%',
                  textStyle: {
                    color: chart.nameColor,
                    fontSize: 15,
                  },
                },
                xAxis: {
                  type: 'category',
                  data: chart.times.map(item => item.substring(0, 10)),
                  axisLabel: {
                    color: '#ccc', // 设置x轴文字颜色为白色
                  },
                  axisLine: {
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                  axisTick: {
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                },
                yAxis: {
                  type: 'value',
                  min: yMin,
                  max: yMax,
                  axisLabel: {
                    color: '#ccc',
                    formatter: function (value) {
                      return value + '' + chart.graphics.unit;
                    },
                  },
                  axisLine: {
                    show: true,
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                  axisTick: {
                    show: true,
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                  splitLine: {
                    lineStyle: {
                      // 设置背景横线
                      color: '#3b44534b',
                    },
                  },
                },
                barCategoryGap: '10%', // 类目之间的间距，默认是 '20%'
                barGap: '0%',
                series: [
                  {
                    type: 'bar',
                    data: chart.data[0].values.map(item => item[1]),
                    barWidth: '20%',
                    itemStyle: {
                      color: params => chart.data[0].color,
                    },
                    label: {
                      show: false, // 默认隐藏
                      position: 'top', // 显示在柱子顶部
                      color: '#ccc', // 自定义文字颜色
                      fontSize: 14,
                      formatter: '{c}', // 显示数值
                    },
                    emphasis: {
                      label: {
                        show: true, // 鼠标移上时显示
                        fontSize: 14,
                        formatter: '{c}', // 显示数值，可用 {b} 名称、{c} 数值、{d}% 百分比
                      },
                    },
                  },
                ],
              };
            } else if (type === 'pie') {
              const chart = this.charts.find(chart => chart.id === id);
              option = {
                backgroundColor: chart.backgroundColor,
                title: {
                  text: chart.name,
                  left: 'left',
                  top: '5%',
                  textStyle: {
                    color: chart.nameColor,
                    fontSize: 15,
                  },
                },
                tooltip: { trigger: 'item' },
                legend: {
                  show: false,
                  top: '5%',
                  left: 'center',
                  textStyle: {
                    color: '#ffffff', // 设置图例文字颜色为红色
                  },
                },
                series: [
                  {
                    type: 'pie',
                    radius: ['40%', '70%'],
                    center: ['50%', '60%'],
                    label: {
                      show: false,
                      position: 'center',
                    },
                    data: chart.data.map(item => ({
                      name: item.name,
                      value: item.value,
                    })),
                    itemStyle: {
                      color: params => chart.data[params.dataIndex].color,
                    },
                  },
                ],
              };
            } else if (type === 'line') {
              let chart = this.charts.find(chart => chart.id === id);
              console.log(chart, '曲线图的数据');
              chart.times = chart.times.map(t => t.slice(11, 19));
              option = {
                backgroundColor: chart.backgroundColor,
                grid: {
                  left: '2%',
                  right: '4%',
                  top: '20%',
                  bottom: '1%',
                  containLabel: true,
                },
                title: {
                  text: chart.name,
                  left: 'left',
                  top: '2%',
                  textStyle: {
                    color: chart.nameColor,
                    fontSize: 15,
                  },
                },
                tooltip: {
                  trigger: 'axis',
                  axisPointer: {
                    type: 'line',
                  },
                },
                xAxis: {
                  type: 'time',
                  boundaryGap: false,
                  axisLabel: {
                    color: '#ccc',
                    formatter: function (value) {
                      const date = new Date(value);
                      const h = date.getHours().toString().padStart(2, '0');
                      const m = date.getMinutes().toString().padStart(2, '0');
                      return m === '00' || (h === '23' && m === '59') ? `${h}:${m}` : '';
                    },
                  },
                  axisLine: {
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                  axisTick: {
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                },
                yAxis: {
                  type: 'value',
                  min: chart.graphics.yMin,
                  max: chart.graphics.yMax,
                  axisLabel: {
                    color: '#ccc',
                  },
                  axisLabel: {
                    color: '#ccc',
                    formatter: function (value) {
                      return value + '' + chart.graphics.unit;
                    },
                  },
                  axisTick: {
                    show: true,
                    lineStyle: {
                      color: '#707070',
                    },
                  },
                  splitLine: {
                    lineStyle: {
                      color: '#3b44534b',
                    },
                  },
                },
                series: chart.data.map(device => ({
                  name: device.name,
                  type: 'line',
                  symbol: 'none',
                  data: device.values.map(item => [new Date(item[0]), item[1]]),
                  itemStyle: {
                    color: device.color,
                  },
                  lineStyle: {
                    width: 2,
                  },
                  areaStyle: {
                    color: device.lineBackgroundColor,
                    opacity: 0.3,
                  },
                })),
              };
            }
            this.chartInstances[id].setOption(option);
          }
        }
      });
    },
    // 监听位置
    onResize(id, newWidth, newHeight) {
      const chart = this.charts.find(chart => chart.id === id);
      if (chart) {
        chart.width = newWidth;
        chart.height = newHeight;
        if (this.chartInstances[id]) {
          this.$nextTick(() => this.chartInstances[id].resize());
        }
      }
    },
    // 监听拖拽
    onDrag(id, x, y) {
      // 不做任何处理
    },
    onDragStop(chartId, x, y) {
      // 拖动完成后，保存最终位置
      const chart = this.charts.find(c => c.id === chartId);
      console.log(chart, x, y);
      if (chart) {
        chart.x = x;
        chart.y = y;
      }
    },
    onTextDrag(id, x, y) {
      const text = this.texts.find(text => text.id === id);
      if (text) {
        text.x = x;
        text.y = y;
      }
    },
    onDisplayImageResize(id, newWidth, newHeight) {
      const image = this.displayImageArray.find(c => c.id === id);
      if (image) {
        image.width = newWidth;
        image.height = newHeight;
      }
    },
    onDisplayImageDragStop(id, x, y) {
      const image = this.displayImageArray.find(c => c.id === id);
      if (image) {
        image.x = x;
        image.y = y;
      }
    },
    editDisplayImage(id) {
      const image = this.displayImabox1geArray.find(c => c.id === id);
      console.log(image);
    },
    handleDisplayImageClick(image) {
      console.log(image, '点击看板image');
      if (image.name == '能耗') {
        this.leftDialogShowContent = '能耗';
        this.$refs.leftDialog.dialogShow = true;
      } else if (image.name == '分数') {
        this.$refs.leftDialog.dialogShow = true;
        this.leftDialogShowContent = '分数';
      } else if (image.name == '摄像头') {
        const playUrl =
          'https://open.ys7.com/jssdk/theme.html?url=ezopen://open.ys7.com/F08316479/1.live&autoplay=1&accessToken=at.77ad9ynn2oykxkb70wq36cm40ddecgy3-8z6v0ieh4m-0usts41-metiy70vf';
        this.createCamera.createCameraPath = playUrl;
        this.dialogVisible = true;
      } else if (image.name == '监控') {
        console.log(11111111111);

        // this.fullOrfloor = false;
        // this.showModelVisiable = true;
      }
    },
    handleDisplayImageSuccess() {
      this.getDisplayImage();
    },
    saveDisplayImage(id) {
      const image = this.displayImageArray.find(c => c.id === id);
      console.log(image, '保存看板image');
      const formData = new FormData();
      formData.set('id', image.id ?? '');
      formData.set('cadTreeId', image.cadTreeId ?? '');
      formData.set('name', image.name ?? '');
      formData.set('x', image.x ?? '');
      formData.set('y', image.y ?? '');
      formData.set('width', image.width ?? '');
      formData.set('height', image.height ?? '');
      // for (let [key, value] of formData.entries()) {
      //   console.log(`${key}: ${value}`);
      // }
      postDisplayImage(formData).then(res => {
        console.log(res);
        if (res.success) {
          this.$message.success('看板修改成功');
          this.getDisplayImage();
        }
      });
      console.log(image);
    },
    editDisplayImage(id) {
      const image = this.displayImageArray.find(c => c.id === id);
      this.$refs.displayAddImage.openVisiable({ image });
    },
    removeDisplayImage(id) {
      delDisplayImage(id).then(res => {
        if (res.success) {
          this.$message.success('看板删除成功');
          this.getDisplayImage();
        }
      });
    },
    handleDisplayTextClick(text) {
      ipcRenderer.send('open-url', text.link);
    },
    onDisplayImageDrag() {},
    onDisplayTextDragStop(id, x, y) {
      const text = this.displayTextArray.find(c => c.id === id);
      if (text) {
        text.x = x;
        text.y = y;
      }
    },
    editDisplayText(id) {
      const text = this.displayTextArray.find(c => c.id === id);
      console.log(text);
      this.displayTextStatus = 'edit';
      this.boardTextForm.text = text.text;
      this.boardTextForm.link = text.link;
      this.boardTextForm.size = text.size;
      this.boardTextForm.color = text.color;
      this.boardTextForm.x = text.x;
      this.boardTextForm.y = text.y;
      this.boardTextForm.bold = text.fontWeight;
      this.displayTextEditId = text.id;
      this.boardTextForm.visiable = true;
    },
    removeDisplayText(id) {
      delDisplayText(id).then(res => {
        console.log(res);
        if (res.success) {
          this.$message.success('看板文本删除成功');
          this.getDisplayText();
        }
      });
    },
    saveDisplayText(id) {
      const text = this.displayTextArray.find(c => c.id === id);
      console.log(text, '保存看板text');
      const data = {
        id: text.id,
        text: text.text,
        link: text.link,
        size: text.size,
        color: text.color,
        x: text.x,
        y: text.y,
        cadTreeId: this.treeLevelCurrentId,
        fontWeight: text.fontWeight,
      };
      postDisplayText(data).then(res => {
        console.log(res);
        if (res.success) {
          this.$message.success('看板文本修改成功');
          this.getDisplayText();
        }
      });
    },
    handleModel(val) {
      switch (val) {
        case 0:
          this.title = '上传背景图';
          this.uploadDialogVisible = true;
          break;
        case 1:
          this.title = '创建饼状图';
          this.addChart('pie');
          break;
        case 2:
          this.title = '创建柱状图';
          this.addChart('bar');
          break;
        case 3:
          this.title = '创建曲线图';
          this.addChart('line');
          break;
        case 4:
          this.title = '创建时间表';
          break;
        case 5:
          this.title = '创建超链接';
          break;
        case 6:
          this.title = '创建文本';
          this.displayTextStatus = 'add';
          this.boardTextForm.x = void 0;
          this.boardTextForm.y = void 0;
          this.boardTextForm.text = '';
          this.boardTextForm.link = '';
          this.boardTextForm.size = '';
          this.boardTextForm.color = '';
          this.boardTextForm.bold = '';
          this.boardTextForm.visiable = true;
          break;
        default:
          this.title = '其他图形';
          break;
      }
    },
    // 能耗代码 - - -- end
    // 修改当前3D界面视角
    updateCamera(settings) {
      console.log(settings, '修改方法：要修改的数据');
      if (settings) {
        const {
          cameraPos,
          targetPos,
          near,
          far,
          minPolarAngle,
          maxPolarAngle,
          enableDamping,
          dampingFactor,
          enableZoom,
          enableRotate,
          color,
        } = settings;
        if (
          cameraPos.x === targetPos.x &&
          cameraPos.y === targetPos.y &&
          cameraPos.z === targetPos.z
        ) {
          this.$message.error('相机位置和目标位置相同，无法看到任何内容！');
          return;
        }
        if (near >= far) {
          this.$message.error('相机 near 值必须小于 far 值！');
          return;
        }
        this.camera.position.set(cameraPos.x, cameraPos.y, cameraPos.z);
        // this.camera.near = near;
        // this.camera.far = far;
        this.camera.updateProjectionMatrix();
        // 设置控制器限制缩放距离
        this.controls.minDistance = near * 10; // 比 near 稍远一点，避免靠太近裁掉模型
        this.controls.maxDistance = far * 0.8; // 比 far 稍小一点，避免推进到不可见
        this.controls.target.set(targetPos.x, targetPos.y, targetPos.z);
        this.controls.minPolarAngle = minPolarAngle;
        this.controls.maxPolarAngle = maxPolarAngle;
        this.controls.enableDamping = enableDamping;
        this.controls.dampingFactor = dampingFactor;
        //this.controls.enableZoom = enableZoom;
        //this.controls.enableRotate = enableRotate;
        this.controls.enableRotate = true;
        this.controls.enablePan = enableZoom;
        this.controls.update();
        this.light.color.set(color);
        this.directionalLight.color.set(color);
      } else {
        this.camera.position.set(1, 1, 1);
        // this.camera.near = near;
        // this.camera.far = far;
        this.camera.updateProjectionMatrix();
        // 设置控制器限制缩放距离
        this.controls.enableZoom = true;

        this.controls.minDistance = 1;
        this.controls.maxDistance = 10000;
        this.controls.enableDamping = true; // ✅ 开启阻尼效果（惯性）
        this.controls.dampingFactor = 0.05; // ✅ 合理的阻尼系数
        this.controls.screenSpacePanning = false;
        this.controls.minPolarAngle = 0; // ✅ 最小仰角（0 允许上下旋转）
        this.controls.maxPolarAngle = Math.PI; // ✅ 最大仰角（PI = 180°）
        this.controls.minAzimuthAngle = -Infinity; // ✅ 方位角无限制
        this.controls.maxAzimuthAngle = Infinity;
        this.controls.enablePan = true; // ✅ 启用平移
        this.controls.update();
        const colors = '#ffffff';
        this.light.color.set(colors);
        this.directionalLight.color.set(colors);
      }
    },
    // 右键3D菜单
    onWindowResize() {
      const width = this.$refs.canvasContainer.clientWidth;
      const height = this.$refs.canvasContainer.clientHeight;
      this.camera.aspect = width / height;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(width, height);
    },
    onMouseMove(event) {
      const rect = this.$refs.canvasContainer.getBoundingClientRect();
      this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
      this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
    },
    async onRightClick(event) {
      if (this.currentViewMode == 'editmode') {
        event.preventDefault();
        this.raycaster.setFromCamera(this.mouse, this.camera);
        const intersects = this.raycaster.intersectObjects(this.scene.children, true);
        if (intersects.length > 0) {
          this.selectedObject = intersects[0].object;

          const rect = this.$refs.canvasContainer.getBoundingClientRect();
          this.menuStyle.top = event.clientY - rect.top + 5 + 'px';
          this.menuStyle.left = event.clientX - rect.left + 5 + 'px';

          const data = this.checkModelDataDevce(this.selectedObject.name);
          const panoramic = this.checkModelData(this.selectedObject.name);
          this.triggerType == 'threeDModel';
          if (data) {
            // 判断该模块上是否添加了设备模板 zyp4
            this.threeDmenu.canClick4 = false;
            this.threeDmenu.canClick3 = true;
            this.threeDmenu.canClick41 = false; // 当有设备时，不能添加全景图
            this.threeDmenu.delete3 = true;
            this.threeDmenu.delete4 = false;
            this.threeDmenu.canClick31 = true; // 删除设备模板显示
          } else {
            if (panoramic) {
              this.threeDmenu.canClick31 = false; // 删除设备模板不显示
              this.threeDmenu.canClick3 = false; // 删除设备模板不显示
              this.threeDmenu.canClick4 = true;
              this.threeDmenu.canClick41 = true; // 当有设备时，不能添加全景
              this.threeDmenu.delete3 = false;
              this.threeDmenu.delete4 = true;
            } else {
              this.threeDmenu.canClick31 = false; // 删除设备模板不显示
              this.threeDmenu.canClick41 = false; // 删除全景图不显示
              this.threeDmenu.canClick3 = true;
              this.threeDmenu.canClick4 = true;
              this.threeDmenu.delete3 = false;
              this.threeDmenu.delete4 = false;
            }
          }
          // 判断是否有绑定数据 zyp4
          const obj = this.formattedModels.find(
            item => item.modelname === this.selectedObject.name
          );
          if (obj) {
            // 对数据进行回显的操作
            this.threeDmenu.canClick2 = true;
            this.threeDBindHuiData = obj;
            this.addCADDataForm.chooseTreeName = obj.dataId;
            this.threeDFormData.low.min = obj.low.min;
            this.threeDFormData.low.max = obj.low.max;
            this.threeDFormData.low.color = obj.low.color;
            this.threeDFormData.mid.min = obj.mid.min;
            this.threeDFormData.mid.max = obj.mid.max;
            this.threeDFormData.mid.color = obj.mid.color;
            this.threeDFormData.high.min = obj.high.min;
            this.threeDFormData.high.max = obj.high.max;
            this.threeDFormData.high.color = obj.high.color;
            console.log(obj, '99999999999999999999999999999999999');
          } else {
            this.threeDmenu.canClick2 = false;
            this.addCADDataForm.chooseTreeName = '';
            this.threeDFormData.low.min = 0;
            this.threeDFormData.low.max = 0;
            this.threeDFormData.low.color = '#FF0202';
            this.threeDFormData.mid.min = 0;
            this.threeDFormData.mid.max = 0;
            this.threeDFormData.mid.color = '#FF0202';
            this.threeDFormData.high.min = 0;
            this.threeDFormData.high.max = 0;
            this.threeDFormData.high.color = '#FF0202';
          }
          this.showMenu = true;
        } else {
          this.showMenu = false;
        }

        // w
        const modelName = encodeURIComponent(this.selectedObject.name);
        try {
          const res = await cadProperty(modelName, this.treeLevelCurrentId);
          const assetList = res.data || [];
          this.tablehreeData = assetList;
          this.hasAsset = assetList.length > 0;

          // 设置菜单项的文本和图标
          if (this.hasAsset) {
            this.assetMenuText = '编辑资产';
            this.assetMenuIcon = require('@/assets/group/edit.png'); // 请替换为“编辑”图标路径
          } else {
            this.assetMenuText = '添加资产';
            this.assetMenuIcon = require('@/assets/menu/tianjiazichan.png');
          }
        } catch (err) {
          console.error('查询资产失败', err);
          this.hasAsset = false;
        }
      }
    },
    handleMenuAction(action) {
      this.triggerType = 'addThreeDModel';
      console.log('窗口大小改变handleMenuAction');
      if (!this.selectedObject) return;
      const name = this.selectedObject.name || this.selectedObject.uuid;
      console.log(this.selectedObject);
      if (action === 'action1') {
        /* alert(`你点击的是模型模块：${name}`); */
        //this.selectThreeDTreeData(name)
        this.ationMenu = false;
        this.threeDDataBindingShow = true;
      } else if (action === 'action2') {
        //  this.selectedObject.material.emissive = new THREE.Color(0xff0000); // 高亮红色
        this.threeDModelDevice(name);
      } else if (action === 'action3') {
        // zyp
        const obj = this.checkModelData(name);
        if (obj) {
          this.$message.warning('该模块已绑定全景视图');
        } else {
          this.threeDModelOpenPanorama(name);
        }
      } else if (action === 'action0') {
        this.relevanceDialog = true;
        this.ationMenu = true;
      } else if (action === 'action11') {
        this.deleteThreeDBindData();
      } else if (action === 'action31') {
        // 删除全景视图 zyp
        const obj = this.checkModelData(name);
        if (obj) {
          this.deletePanorama(obj.id);
        } else {
          this.$message.warning('该模块没有绑定全景视图');
        }
      } else if (action === 'action12') {
        const data = this.checkModelDataDevce(name);
        if (data && data.id) {
          deletedeviceTemplate(data.id).then(res => {
            if (res.success) {
              this.$message.success('删除成功');
              this.selectThreeDTreeData();
            } else {
              this.$message.error('删除失败');
            }
          });
        } else {
          this.$message.warning('该模块未绑定设备模板');
        }
      }
      this.showMenu = false;
    },
    onMenuClick(event) {
      event.stopPropagation();
    },
    onDocumentClick(event) {
      const menuEl = this.$el.querySelector('.custom-menu');
      if (this.showMenu && (!menuEl || !menuEl.contains(event.target))) {
        this.showMenu = false;
      }
    },
    // 关闭3D模型绑定数据弹窗
    threeDDataBindingShowClose() {
      this.threeDDataBindingShow = false;
    },
    // 更新3D模型上的颜色
    updateColorsByData(dataArray) {
      if (!this.currentModel) return;
      dataArray.forEach(item => {
        const { modelname, datavalie, low, mid, high } = item;
        let color = null;

        if (datavalie >= low.min && datavalie <= low.max) {
          color = low.color;
        } else if (datavalie >= mid.min && datavalie <= mid.max) {
          color = mid.color;
        } else if (datavalie >= high.min && datavalie <= high.max) {
          color = high.color;
        } else {
          console.warn(`数据值 ${datavalie} 不在任何区间内，modelname: ${modelname}`);
          return;
        }
        const targetMesh = this.currentModel.getObjectByName(modelname);
        if (targetMesh && targetMesh.material) {
          targetMesh.material = targetMesh.material.clone();
          targetMesh.material.color.set(color);
          targetMesh.material.needsUpdate = true;
          targetMesh.material.transparent = true; // 关键：开启透明通道
          targetMesh.material.opacity = 0.3; // 设置透明度（0.0 ~ 1.0）
        } else {
          // console.warn('找不到模型部件:', modelname);
        }
      });
    },
    // 3D模型绑定数据后查询
    simulateBackendUpdates() {
      if (this.nodeData.id) {
        let cadId = this.nodeData.id;
        let dataArray = [];
        threeDQueryBindData(cadId).then(res => {
          dataArray = res.data;
          // 整理后端返回的数据
          this.transformData(dataArray);
          // console.log(this.formattedModels, '整理后的数据');
          setTimeout(() => {
            this.updateColorsByData(this.formattedModels);
          }, 500);
        });
      } else {
        return;
      }
    },
    // 3D模型绑定数据
    threeDDataBindingOk() {
      this.threeDDataBindingShow = false;
      console.log(this.nodeData, '右侧的树目录');
      console.log(this.addCADDataForm, '绑定的数据');
      console.log(this.selectedObject, '模型选中的对象');
      let obj = {
        cadTreeId: this.nodeData.id,
        modelName: this.selectedObject.name,
        dataId: this.addCADDataForm.chooseTreeDataId,
        redisKey: this.addCADDataForm.redisKey,
        mqttId: this.addCADDataForm.mqttId,
        models: [
          {
            name: 'low',
            color: this.threeDFormData.low.color,
            value1: this.threeDFormData.low.min,
            value2: this.threeDFormData.low.max,
          },
          {
            name: 'mid',
            color: this.threeDFormData.mid.color,
            value1: this.threeDFormData.mid.min,
            value2: this.threeDFormData.mid.max,
          },
          {
            name: 'high',
            color: this.threeDFormData.high.color,
            value1: this.threeDFormData.high.min,
            value2: this.threeDFormData.high.max,
          },
        ],
      };
      // 判断是否是编辑绑定数据true为编辑
      if (this.threeDmenu.canClick2) {
        obj.dataId = this.threeDBindHuiData.dataId;
        obj.redisKey = this.threeDBindHuiData.redisKey;
        obj.mqttId = this.threeDBindHuiData.mqttId;
        obj.id = this.threeDBindHuiData.id;
      }
      threeDBindData(obj).then(res => {
        this.$message.success(res.data);
        this.simulateBackendUpdates();
      });
    },
    //整理后端返回的数据格式
    transformData(rawData) {
      this.formattedModels = rawData.map(item => {
        const model = {
          modelname: item.modelName,
          datavalie: Number(item.value),
          dataId: item.dataId,
          id: item.id,
          low: {},
          mid: {},
          high: {},
          mqttId: item.mqttId,
          redisKey: item.redisKey,
        };

        item.models.forEach(m => {
          const obj = {
            min: Number(m.value1),
            max: Number(m.value2),
            color: m.color,
          };
          if (m.name === 'low') model.low = obj;
          else if (m.name === 'mid') model.mid = obj;
          else if (m.name === 'high') model.high = obj;
        });

        return model;
      });
    },
    // 删除绑定的数据
    deleteThreeDBindData() {
      let name = this.selectedObject.name;
      const obj = this.formattedModels.find(item => item.modelname === name);

      if (obj) {
        const id = obj.id;
        deleteThreeDQueryBindData(id)
          .then(res => {
            this.$message.success(res.data);
            this.simulateBackendUpdates();
          })
          .catch(err => {
            console.error('删除失败:', err);
            this.$message.error('删除失败，请稍后重试');
          });
      } else {
        this.$message.warning(`模块 "${name}" 未绑定数据，无法执行删除操作`);
      }
    },
    // 查询3D模型上绑定的全景图 zyp
    checkModelData(modelName) {
      const result = this.panoramaArrayThree.find(item => item.modelName === modelName);
      if (result) {
        // 做一些处理，比如展示全景图
        console.log('找到的数据：', result);
        return result;
      } else {
        //this.$message.warning("该模型未绑定全景视图或设备模板数据");
        this.showMenu = false;
      }
    },
    // 实时刷新全景图中的数据 zyp2
    async refreshPanoramaDataMarkers() {
      if (!this.markersPlugin || !this.panoramaCurrentID) return;
      // 先移除所有 'data' 类型的 marker（通常以 text_ 或 img_ 开头，但你可以设定统一命名规则）
      const allMarkers = this.markersPlugin.getMarkers();
      console.log(allMarkers, 'allMarkers11111111111111111');
    },
    // 查询当前目录上的设备模板信息 zyp3
    selectThreeDTreeData() {
      selectDeviceTemplate(this.nodeData.id).then(res => {
        console.log(res, '11111133333333333333333333333');
        this.selectDeviceDataOk = res.data;
      });
    },
    // 根据modelName查询设备模板信息 zyp3
    checkModelDataDevce(modelName) {
      return this.selectDeviceDataOk.find(item => item.modelName === modelName);
    },
    onConfirmAddButton() {},
    addButton(btnData) {
      // 给新按钮一个初始位置
      console.log(this.buttons, 'this.buttons');
      this.buttons.push({
        ...btnData,
        x: 100,
        y: 100,
      });
    },
    startDrag(e, btn) {
      this.dragTarget = btn;
      this.offsetX = e.clientX - btn.x;
      this.offsetY = e.clientY - btn.y;

      document.addEventListener('mousemove', this.onDragBtn);
      document.addEventListener('mouseup', this.stopDragBtn);
    },
    onDragBtn(e) {
      if (!this.dragTarget) return;
      this.dragTarget.x = e.clientX - this.offsetX;
      this.dragTarget.y = e.clientY - this.offsetY;
    },
    stopDragBtn() {
      this.dragTarget = null;
      document.removeEventListener('mousemove', this.onDragBtn);
      document.removeEventListener('mouseup', this.stopDragBtn);
    },
    removeBtn(btn, index) {
      if (btn.id) {
        this.buttons.splice(index, 1);
        delPageLink(btn.id).then(res => {
          this.$message.success(res.data);
          this.selectBtn();
        });
      } else {
        this.buttons.splice(index, 1);
      }
    },
    editBtn(btn) {
      // 可以打开你的子弹窗，传入 `btn` 数据进行编辑
      // this.showAddButtonDialog = true
      this.$refs.addbtnbox.updateBtn(btn, this.treeLevelData);
    },
    saveBtn(btn) {
      let saveBtnObj = new FormData();
      saveBtnObj.set('template', btn.template);
      saveBtnObj.set('name', btn.name);
      saveBtnObj.set('size', btn.size);
      saveBtnObj.set('y', btn.y);
      saveBtnObj.set('x', btn.x);
      saveBtnObj.set('color', btn.color);
      saveBtnObj.set('height', btn.height);
      saveBtnObj.set('width', btn.width);
      saveBtnObj.set('currentCadTreeId', this.nodeData.id);
      if (btn.hasOwnProperty('id')) {
        saveBtnObj.set('id', btn.id);
        saveBtnObj.set('targetCadTreeId', btn.targetCadTreeId);
      } else {
        saveBtnObj.set('targetCadTreeId', btn.nodeData.id);
      }
      if (btn.file) {
        saveBtnObj.set('file', btn.file);
      }
      savePageLink(saveBtnObj).then(res => {
        this.$message.success(res.message);
        this.selectBtn();
      });
    },
    // 查询当前界面按钮
    selectBtn() {
      selectPageLink(this.nodeData.id).then(res => {
        this.buttons = Array.isArray(res.data) ? res.data : [];
      });
    },
    // 查询当前页面的所有图片
    getDisplayImage() {
      getDisplayImages(this.treeLevelCurrentId).then(res => {
        console.log(res, '查询当前页面所有看板图片');
        if (res.success) {
          this.displayImageArray = res.data;
        }
      });
    },
    findNodeById(id, nodes = this.treeLevelData) {
      for (const node of nodes) {
        if (node.id == id) {
          return node;
        } else if (node.child && node.child.length) {
          const found = this.findNodeById(id, node.child);
          if (found) {
            return found;
          }
        }
      }
      return null;
    },
    async clickBtn(btn) {
      console.log(btn, 'btn');
      if (btn.name == '1层') {
        this.setCut(-3.6);
      } else if (btn.name == '1M层') {
        this.setCut(-2.8);
      } else if (btn.name == '2层') {
        this.setCut(-2);
      } else if (btn.name == '2M层') {
        this.setCut(-1.4);
      } else if (btn.name == '3层') {
        this.setCut(-0.8);
      } else if (btn.name == '3M层') {
        this.setCut(0);
      } else if (btn.name == '4层') {
        this.setCut(0.7);
      } else if (btn.name == '4M层') {
        this.setCut(1.4);
      } else if (btn.name == 'B1层') {
        this.setCut(-4.6);
      } else if (btn.name == 'B2层') {
        this.setCut(-5.8);
      } else if (btn.name == '全楼') {
        this.setCut(25);
      } else if (btn.name == '监控') {
        console.log(11111111111);
        this.fullOrfloor = false;
        this.showModelVisiable = true;
      } else if (btn.name == '报警') {
        this.alarmModuleVisiable = true;
      } else if (btn.name == '刨切') {
        this.setCut(3);
      } else {
        const node = this.findNodeById(btn.targetCadTreeId);
        if (node) {
          this.handleRightNodeClick(node);
        } else {
          console.warn('没找到');
        }
      }
    },
    addTimeChart(data) {
      this.timeCharts.push({
        ...data,
        x: 100,
        y: 100,
      });
    },
    removeTimeChart(index) {
      this.timeCharts.splice(index, 1);
    },
    startDragTime(e, index) {
      this.dragging = true;
      this.dragIndex = index;
      this.offsetX = e.clientX - this.timeCharts[index].x;
      this.offsetY = e.clientY - this.timeCharts[index].y;
      document.addEventListener('mousemove', this.onDragTime);
      document.addEventListener('mouseup', this.stopDragTime);
    },
    onDragTime(e) {
      if (!this.dragging) return;
      const chart = this.timeCharts[this.dragIndex];
      chart.x = e.clientX - this.offsetX;
      chart.y = e.clientY - this.offsetY;
    },
    stopDragTime() {
      this.dragging = false;
      document.removeEventListener('mousemove', this.onDragTime);
      document.removeEventListener('mouseup', this.stopDragTime);
    },
    editTimeChart(time) {
      this.$refs.timeChartRef.openTimeDialog();
    },
    saveTimeChart(time) {
      this.$message.success('保存成功');
    },

    // reg start
    handleEnergyCloseClick() {
      console.log('leftdialog', 111);

      this.$refs.leftDialog.dialogShow = false;
    },
    deviceClickTest() {
      console.log(11111);
      this.showDeviceData[0].label = '健康指数';
      this.leftDialogShowContent = 'showDevice';
      console.log(this.leftDialogShowContent);
    },
    systemClickTest() {
      this.showDeviceData[0].label = '健康指数';
      this.leftDialogShowContent = 'showDevice';
    },
    environmentClickTest() {
      this.showDeviceData[0].label = '健康指数';
      this.leftDialogShowContent = 'showDevice';
    },
    deviceTargetClickTest() {
      this.showDeviceData[0].label = '国家科技传播中心能源指标';
      this.leftDialogTargetShowTree = true;
      this.leftDialogTargetShowDetail = false;
    },
    systemTargetClickTest() {
      // this.leftDialogShowContent = 'showTargetYujing';
      this.showDeviceData[0].label = '国家科技传播中心运行指标';
      this.leftDialogTargetShowTree = true;
      this.leftDialogTargetShowDetail = false;
    },
    environmentTargetClickTest() {
      // this.leftDialogShowContent = 'showTargetCilun';
      this.showDeviceData[0].label = '国家科技传播中心能效水平';
      this.leftDialogTargetShowTree = true;
      this.leftDialogTargetShowDetail = false;
    },
    handleEnergyNodeClick(line) {
      console.log(line, 'line');
      if (
        line.label == '国家科技传播中心设备状况' ||
        line.label == '国家科技传播中心系统状况' ||
        line.label == '国家科技传播中心环境状况'
      ) {
      } else if (line.label == '1层') {
        this.setCut(-3.6);
      } else if (line.label == '1M层') {
        this.setCut(-2.8);
      } else if (line.label == '2层') {
        this.setCut(-2);
      } else if (line.label == '2M层') {
        this.setCut(-1.4);
      } else if (line.label == '3层') {
        this.setCut(-0.8);
      } else if (line.label == '3M层') {
        this.setCut(0);
      } else if (line.label == '4层') {
        this.setCut(0.7);
      } else if (line.label == '4M层') {
        this.setCut(1.4);
      } else {
        this.leftDialogShowContent = '能耗';
      }
    },
    handleTestRightMenuShowClick() {
      console.log(1111111111);
    },
    handleLeftDialogShowDetail() {
      this.leftDialogTargetShowTree = false;
      this.leftDialogTargetShowDetail = true;
    },
    handleAlarmClose() {
      this.alarmModuleVisiable = false;
    },
    submitCustomAlarm() {},
  },
};
</script>
<style scoped>
.energy-close-box {
  display: flex;
  justify-content: flex-end;
}
.energy-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin-left: 20px;
  margin-top: 50px;
}
.energy-close {
  font-size: 20px;
  cursor: pointer;
  z-index: 99999;
  margin-right: 20px;
}
.tem-device,
.tem-system,
.tem-environment {
  width: 300px;
  height: 230px;
  margin-bottom: 20px;
  border-radius: 12px;
  border: 1.5px solid #e0e0e0; /* 默认边框 */
  background: transparent; /* 去掉白色背景 */
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
  transition: all 0.3s ease;
  padding: 16px;
  box-sizing: border-box;
  font-family: 'Segoe UI', 'PingFang SC', sans-serif;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

/* 不同模块边框色 */
.tem-device {
  border-color: #71aaeb;
}

.tem-system {
  border-color: #71aaeb;
}

.tem-environment {
  border-color: #71aaeb;
}

/* 悬停效果：加粗边框 & 阴影加深 */
.tem-device:hover,
.tem-system:hover,
.tem-environment:hover {
  border-width: 2px;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.12);
  transform: translateY(-3px);
}

.tem-device p,
.tem-system p,
.tem-environment p {
  font-size: 28px;
  color: #c9c6c6;
  line-height: 1.6;
  text-align: center;
}

.showDevice {
  font-size: 30px;
}

.device-box1 {
  display: flex;
  justify-content: space-between;
}
.device-box1 p {
  margin-top: 10px;
  margin-left: 20px;
  font-size: 16px;
}
.device-box2 {
  display: flex;
  justify-content: space-between;
}
.device-box2 p {
  margin-top: 10px;
  margin-left: 20px;
  font-size: 16px;
}
.system-box1 p {
  margin-top: 10px;
  font-size: 16px;
}
.environment-box1 {
  display: flex;
  justify-content: space-between;
}
.environment-box1 p {
  margin-top: 10px;
  margin-left: 20px;
  font-size: 16px;
}
.environment-box2 {
  display: flex;
  justify-content: space-between;
}
.environment-box2 p {
  margin-top: 10px;
  margin-left: 20px;
  font-size: 16px;
}

.target-device {
  display: flex;
  justify-content: space-around;
  border-bottom: 2px solid #ccc;
}

.tem-target-device,
.tem-target-system,
.tem-target-environment {
  /* border: 1px solid #71aaeb; */
  /* border-radius: 10px; */
  position: relative;
  width: 100px;
  height: 100px;
  font-size: 16px;
  letter-spacing: 4px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-family: Arial, Helvetica, sans-serif;
}

.tem-target-device .tem-target-span,
.tem-target-system .tem-target-span,
.tem-target-environment .tem-target-span {
  position: absolute;
  bottom: 20px;
  font-size: 16px;
}

.target-device img {
  width: 45px;
  height: 45px;
  margin-top: 10px;
}
.targetShowTree {
  margin-top: 20px;
  margin-left: 20px;
  position: relative;
}
.target-line {
  width: 2px;
  height: 85px;
  background-color: #ccc;
  margin-top: 15px;
  margin-left: 5px;
}
.targetShowTree .target-extend {
  position: absolute;
  font-size: 14px;
  margin-left: 200px;
  color: #ccc;
  margin-top: -5px;
}
.tree-node {
  display: flex;
  justify-content: space-between; /* 左右分布 */
  width: 270px; /* 给个固定宽度，数字就能对齐 */
}

.tree-label {
  flex: 1; /* 占据左边 */
  white-space: nowrap;
}

.tree-extra {
  min-width: 40px; /* 数字区域固定宽度 */
  text-align: right; /* 靠右对齐 */
  color: #ee0524;
  font-size: 13px;
}
.section {
  border-radius: 6px;
  padding: 12px;
  margin-bottom: 12px;
}
.section h2 {
  margin: 0 0 8px;
  font-size: 16px;
}
.section p {
  margin: 4px 0;
  font-size: 14px;
}
</style>
<style scoped>
.device-list {
  list-style: none;
  padding: 0;
  margin: 0 0 15px 0;
}

.device-list li {
  margin-bottom: 15px;
}

.device-list .system {
  display: inline-block;
  width: 120px;
  font-weight: 600;
  color: #a0cfff;
}

.device-list .score {
  display: inline-block;
  width: 60px;
  font-weight: bold;
  color: #ffffff;
}

.progress-bar {
  position: relative;
  height: 8px;
  width: 100%;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  margin-top: 4px;
}

.progress-fill {
  height: 100%;
  background: #00c8ff;
  border-radius: 4px;
  transition: width 0.5s ease;
}

::v-deep .el-table th.el-table__cell {
  background-color: rgb(26, 26, 28);
}

/deep/ .el-dialog {
  background: #252526;
}

/deep/ .el-dialog__title {
  color: #ece6e6;
  font-weight: bold;
}

/deep/ .el-form-item__label {
  color: #1b75c9;
  font-size: 14px;
  font-weight: bold;
}

/deep/ .no-border-input .el-input__inner {
  border: none;
  box-shadow: none;
}

.vjContextMenu {
  width: 150px !important;
  border: 0.1px solid #0346a2;
  box-shadow: none !important;
}

.vjContextMenuItemIcon {
  margin-left: 10px;
  margin-right: 5px;
}

.vjContextMenuItemIcon:first-child {
  width: 30px !important;
  height: 30px !important;
}

.video-active {
  width: 20px;
  height: 20px;
  background-image: url('../assets/visual_images/r_menu/dengguang_act.png');
  background-size: 100% 100%;
}

.test {
  background-color: #000a27;
}

/deep/ .el-dialog {
  border: 1px solid #3a3a3c;
  box-shadow: 0 0 8px rgba(90, 90, 92, 0.5);
}

/deep/ .el-dialog__header {
  padding: 10px;
  padding-bottom: 10px;
}

/deep/ .el-dialog__headerbtn {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 0;
  background: transparent;
  border: none;
  outline: none;
  cursor: pointer;
  font-size: 16px;
}

/deep/ .el-input__inner {
  height: 23px;
  line-height: 23px;
  background: #101418;
  border: 1px solid #101418;
  color: #fff;
}

/deep/ .el-input__inner:focus {
  border-color: #101418;
}

/deep/ .el-form-item {
  margin-bottom: 10px;
}

/deep/ .el-input__inner::placeholder {
  color: #606266;
}

/deep/ .el-button--medium {
  padding: 5px 15px;
}

/deep/ .el-dialog__body {
  padding: 0px 20px 0px 20px;
}

/deep/ .el-dialog__title {
  margin-left: 10px;
  color: #fbfbfb;
  font-size: 11px;
}

/deep/ .el-form-item__label {
  color: #fbfbfb;
  font-size: 11px;
}

/deep/ .el-form-item {
  margin-bottom: 5px;
}

/deep/ .el-color-picker__trigger {
  margin-top: 8px;
  height: 20px;
  width: 20px;
  padding: 0;
  border: none;
}

/deep/ .el-radio__label {
  font-size: 11px !important;
  color: #fbfbfb;
  margin-top: -2px;
}

/deep/ .el-radio__inner {
  width: 12px;
  height: 12px;
}

/deep/ .el-radio__input {
  margin-top: 2px;
}

/deep/ .el-icon-close,
/deep/ .el-icon-edit-outline,
/deep/ .el-icon-check {
  color: #dddddd;
  font-size: 16px;
}

.addDataBtnBox {
  margin-right: 15px;
  z-index: 999;
  position: absolute;
  top: 10px;
  right: 32px;
  cursor: pointer;
}

.addDataBtnZen {
  margin-right: 45px;
  z-index: 999;
  position: absolute;
  top: 10px;
  right: 32px;
  cursor: pointer;
}

.addDataBtnCloseBox {
  margin-right: 5px;
}

.selected {
  border: 1px solid #64c8fc;
}

.selectedPanoramaIcon {
  border: 1px solid #64c8fc;
}

.dialog-title {
  font-size: 11px !important;
  color: #fbfbfb;
}

/deep/ .el-dialog__footer {
  padding: 10px;
}

.choose-data /deep/ .el-form-item__label {
  color: #1890ff;
  cursor: pointer;
}

.addDataText /deep/ .el-form-item {
  width: 50px;
  margin-left: 0px;
}

.panorama /deep/ .el-dialog__title {
  position: sticky;
  z-index: 9;
}

.panorama /deep/ .el-dialog__body {
  height: 400px;
  padding: 0;
}

.panorama-body {
  position: absolute;
  top: 0px;
}

.panorama /deep/ .el-dialog__close {
  color: #fbfbfb;
}

.panorama /deep/ .el-dialog__headerbtn {
  z-index: 9;
}

.upload-text {
  font-size: 11px;
  color: #1890ff;
  cursor: pointer;
}

.upload-name {
  margin-top: 10px;
  display: flex;
  text-align: center;
  font-size: 11px;
  color: #fbfbfb;
}

.upload-name-box {
  margin-top: 10px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  font-size: 11px;
  color: #fbfbfb;
}

.panorama-bindData {
  color: #1890ff;
  font-size: 11px;
  margin-left: 20px;
  cursor: pointer;
}

.QR-code-box {
  margin-top: 10px;
  width: 200px;
  height: 200px;
}

.QR-code-box img {
  width: 100%;
  height: 100%;
}

.edit-panorama /deep/ .edit-panorama-body {
  height: 60px;
}

.btn-panorama /deep/ .edit-panorama-body {
  height: 60px;
}

.bookmark /deep/ .bookmark-body {
  height: 620px;
}

.bookmark /deep/ .bookmark-body .mome-text {
  all: unset;
  background-color: rgb(37, 37, 38);
  color: #ffffff;
  font-size: 11px;
  width: 100%;
  height: 377px;
  cursor: text;
}

.bookmark /deep/ .el-dialog__body {
  padding: 0;
}

.edit-space /deep/ .edit-panorama-body {
  height: 110px;
}

.edit-relevance /deep/ .edit-panorama-body {
  padding-bottom: 10px;
}

.edit-asset /deep/ .edit-panorama-body {
  height: 60px;
}

.new-cad /deep/ .edit-panorama-body {
  height: 110px;
}

.delete-cad /deep/ .edit-panorama-body {
  height: 50px;
}

.delete-space /deep/ .edit-panorama-body {
  height: 45px;
}

.right-tree /deep/ .edit-panorama-body {
  height: 68px;
}

.tree-show-class /deep/ .el-dialog__body {
  padding-bottom: 30px;
}

.tree-dialog-class /deep/ .el-dialog__body {
  padding-bottom: 30px;
}

.add-panorama-dialog /deep/ .edit-panorama-body {
  height: 100px;
}

.add-camera-dialog /deep/ .edit-panorama-body {
  height: 120px;
}

.add-device-dialog /deep/ .edit-panorama-body {
  height: 80px;
}

.camera-x,
.camera-y,
.camera-z {
  display: flex;
  align-items: center;
}

.delete-tree-space /deep/ .edit-panorama-body {
  height: 35px;
}

.panorama .panorama-body {
  height: 400px;
}

.cad-device /deep/ .el-dialog__body {
  height: 500px;
  padding: 0 18px;
}

.create-group-box {
  margin-top: -6px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.create-group-page-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}

.create-head {
  display: flex;
  justify-content: flex-start;
  margin-top: 5px;
}

.create-group-text {
  margin-left: -10px;
  font-family: Source Han Sans SC;
  font-weight: 400;
  font-size: 12px;
  cursor: pointer;
}

.create-group-img {
  width: 16px;
  height: 16px;
}

.create-icon i {
  margin-left: 5px;
}

/deep/ .el-popover .custom-popover {
  background-color: red;
  font-size: 16px;
}

.character-box-create {
}

.character-box-create-box {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.character-box-create-group-img {
  width: 12px;
  height: 12px;
}

.character-box-create-group-text {
  margin-top: 2px;
  margin-left: 5px;
  color: white;
}

.context-menu {
  position: fixed;
  z-index: 999;
  background: #2c2c2c;
  padding: 8px 0;
  border-radius: 6px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
  min-width: 130px;
}

.menu-item {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  color: white;
  cursor: pointer;
}

.menu-item.disabled {
  color: rgb(91, 91, 91);
  cursor: not-allowed !important;
  pointer-events: none;
  background-color: transparent !important;
}

.menu-item:hover {
  background-color: rgb(58, 58, 58);
}

.menu-item.disabled:hover {
  cursor: not-allowed !important;
  background-color: inherit;
}

.menu-img {
  width: 16px;
  height: 16px;
  margin-right: 8px;
}

.menu-text {
  font-size: 11px;
}

.angleViewBox {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 350px;
  background-color: lightblue;
}

.level-tree >>> .el-tree-node__content:hover,
.level-tree >>> .el-tree-node.is-current > .el-tree-node__content {
  background-color: rgb(47, 47, 47) !important;
  color: #fff;
}

.level-tree >>> .el-tree-node:focus > .el-tree-node__content {
  background-color: transparent !important;
}
</style>
<style>
.vjmapgis-popup-content {
  background: rgb(44, 44, 44);
  box-shadow: 0 0 8px rgba(167, 165, 165, 0.5);
  padding: 5px;
  font-size: 11px;
  top: -5px;
}

.devices {
  width: 19px;
  height: 19px;
  background-image: url('../assets/visual_images/r_menu/muban_active.png');
  background-size: 100% 100%;
}

.panorama-marker,
.camers-marker {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.panoramas {
  width: 17px;
  height: 17px;
  background-size: 100% 100%;
}

.camers {
  width: 21px;
  height: 21px;
  background-size: 100% 100%;
}

.camers-label {
  font-size: 10px;
  color: #64c8fc;
  white-space: nowrap;
}

.video-popup .vjmapgis-popup-content {
  background-color: transparent;
}

.vjmapgis-popup-tip {
  display: none;
}

.device-popup .vjmapgis-popup-content {
  background-color: transparent;
}

.psv-button-svg {
  transform: scale(0.7);
}

.psv-navbar {
  height: 24px;
}

.psv-button {
  margin-top: -8px;
}

.psv-zoom-range.psv-button {
  margin-top: 2px;
}
</style>
<style lang="scss" scoped>
$text_family: SourceHanSansCN;
$text_weight: 400;
$text_size: 12px;
$text_color: #f5f5f5;
$hover_bg: #272f3a;

.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: all 0.5s ease;
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  opacity: 0;
  transform: translateY(-5px);
}

.visual {
  height: 100%;
  background: #212830;
  overflow: hidden;
  user-select: none;

  .herader {
    width: 100%;
    height: auto;

    .navbar {
      height: 45px;
      background: #212830;
      overflow: hidden;

      .navbar-basics-yunwei {
        width: 170px !important;
      }

      .navbar-week-plans {
        margin-left: 2px;
        width: 330px;
        background: #3b4453;
        display: flex;
        justify-content: space-around;
        align-items: center;

        .navbar-plans {
          display: flex;
          justify-content: center;
          align-items: center;
          flex-direction: column;

          .plans-image {
            margin-top: 3px;
            width: 17px;
            height: 17px;
          }

          .plans-alarm {
            margin-top: 5px;
            width: 15px;
            height: 15px;
          }

          p {
            margin-top: 3px;
            font-weight: 400;
            font-size: 10px;
            color: #f5f5f5;
          }
        }
      }

      .navbar-add {
        margin-left: 2px;
        width: 40px;
        background: #3b4453;
        overflow: hidden;
        display: flex;
        justify-content: center;
        align-items: center;

        .banner-add {
          width: 16px;
          height: 16px;
        }
      }

      .navbar-basics {
        width: 280px;
        background: #3b4453;
        display: flex;
        flex-direction: column;
        justify-content: space-between;

        .basics-box {
          padding: 0 10px;

          .basics-specific:hover {
            background: $hover_bg;
          }

          .basics-specific {
            padding: 5px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            white-space: nowrap;

            .icon {
              width: 17px;
              height: 17px;
            }

            .renyuan-icon {
              margin-top: 3px;
              width: 14px;
              height: 14px;
            }

            .text {
              font-family: $text_family;
              font-weight: $text_weight;
              color: $text_color;
              font-size: 10px;
              margin-top: 4px;
            }
          }
        }

        .basics {
          .basics-wrap:hover {
            background: $hover_bg;
          }

          .basics-wrap {
            margin-bottom: 5px;
            padding: 1px 5px;

            .text {
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
            }

            .icon {
              margin-left: 5px;
              width: 7px;
              height: 5px;
            }
          }
        }
      }

      .navbar-function {
        margin-left: 2px;
        background: #3b4453;
        width: 256px;

        .function-box {
          padding-top: 5px;
          padding-right: 5px;
          display: flex;
          flex-wrap: wrap;
          justify-content: center;

          .function-specific:hover {
            background: $hover_bg;
          }

          .function-specific {
            padding: 5px;
            margin-top: 8px;
            margin-left: 6px;

            .icon {
              width: 16px;
              height: 16px;
            }

            .text {
              margin-left: 3px;
              font-family: Source Han Sans SC;
              font-weight: 400;
              font-size: 11px;
              color: #f5f5f5;
              line-height: 9px;
              text-wrap: nowrap;
            }
          }
        }

        .function {
          .function-wrap:hover {
            background: $hover_bg;
          }

          .function-wrap {
            margin-bottom: 5px;
            padding: 1px 5px;

            .text {
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
            }

            .icon {
              margin-left: 5px;
              width: 7px;
              height: 5px;
            }
          }
        }
      }

      .navbar-coverage {
        padding: 0 10px;
        background: #3b4453;

        #coverage-top-custom-box {
          width: 262px;
        }

        .coverage-top-box {
          margin-top: 3px;
          width: 356px;
          height: 28px;
          border: 0.1ch solid #5b6378;
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 0 7px;

          .img-rotate {
            animation: all 1s;
            transform: rotate(180deg);
          }

          .coverage-bulb {
            display: flex;
            justify-content: flex-start;
            align-items: center;

            .bulb {
              width: 19px;
              height: 19px;
            }

            .coverage-des {
              padding-left: 5px;
              font-family: Source Han Sans SC;
              font-weight: 400;
              font-size: 10px;
              color: #f5f5f5;
              cursor: pointer;
            }
          }
        }

        .coverage-box {
          margin-top: 10px;
          height: 40px;
          padding: 0 10px;

          .coverage-single:hover {
            background: $hover_bg;
          }

          .coverage-single {
            display: flex;
            padding: 5px;

            .text {
              margin-left: 5px;
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
            }
          }

          .control {
            .btn {
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
              margin-top: 5px;
              background-color: #50617f;
              padding: 5px;
            }
          }
        }

        .coverage {
          .coverage-wrap:hover {
            background: $hover_bg;
          }

          .coverage-wrap {
            margin-bottom: 5px;
            padding: 1px 5px;

            .text {
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
            }

            .icon {
              margin-left: 5px;
              width: 7px;
              height: 5px;
            }
          }
        }
      }

      .navbar-weather {
        margin-left: 2px;
        width: 100%;
        background: #3b4453;
        position: relative;

        .navbar-weather-layout {
          position: absolute;
          right: 0;
          border-left: 2px solid #222933;
          height: 99px;
          padding: 0 10px;
        }

        .navbar-property-layout {
          position: absolute;
          right: 0px;
          border-left: 2px solid #222933;
          padding-left: 5px;
          display: flex;
          justify-content: space-between;
          align-items: center;

          .navbar-basics {
            width: 370px;

            .flex-between-center-yunwei {
              display: flex;
              justify-content: flex-end;
              align-items: center;
            }

            .icon {
              width: 20px;
              height: 20px;
            }

            .text {
              font-size: 10px;
            }
          }

          .navbar-yunwei {
            width: 70px;
          }

          .property-basics {
            width: 180px;

            .icon {
              margin-top: 3px;
              width: 15px;
              height: 15px;
            }
          }
        }

        .weather-box {
          margin-top: 18px;
          height: 40px;

          .show {
            font-family: PingFang SC;
            font-weight: 400;
            font-size: 11px;
            color: #f5f5f5;
          }

          .text {
            font-family: Source Han Sans SC;
            font-weight: 400;
            font-size: 11px;
            color: #f5f5f5;
          }

          .humidity {
            margin-left: 11px;
          }

          .weather-show {
            .weather-img {
              width: 28px;
              height: 22px;
              margin-top: -5px;
            }
          }
        }

        .weather {
          margin: 0 auto;

          .weather-wrap {
            margin-bottom: 5px;

            .text {
              font-family: $text_family;
              font-weight: $text_weight;
              font-size: $text_size;
              color: $text_color;
            }

            .icon {
              margin-left: 5px;
              width: 7px;
              height: 5px;
            }
          }
        }
      }
    }
  }

  .tab {
    margin-top: 1px;
    height: 27px;
    padding-left: 16px;
    background: #212830;

    .catalogue-img {
      width: 16px;
      height: 12px;
    }

    .separator-img {
      margin-left: 9px;
      width: 9px;
      height: 16px;
    }

    .begin {
      margin-left: 9px;
      font-family: SourceHanSansCN;
      font-weight: 400;
      font-size: 11px;
      color: #f5f5f5;
      line-height: 9px;
    }

    .tab-box {
      background: #3b4453;
      height: 24px;
      margin-left: 20px;
      position: relative;

      .xiebian-one {
        position: absolute;
        left: -10px;
      }

      .tab-text {
        font-family: SourceHanSansCN;
        font-weight: 400;
        font-size: 11px;
        color: #f5f5f5;
        line-height: 9px;
        margin-left: 8px;
      }

      .close {
        margin-left: 18px;
        width: 14px;
        height: 14px;
      }

      .xiebian-two {
        position: absolute;
        right: -10px;
      }
    }

    .add-img {
      width: 14px;
      height: 14px;
      margin-left: 19px;
    }
  }

  .main {
    width: 100%;
    // height: calc(100% - 189px);
    height: 100%;
    background: #212830;
    position: relative;

    .header-area {
      height: 1px;
      background: #3b4453;
    }

    .content {
      height: 100%;
      background-repeat: repeat;
      background-position: 0 -8px;

      .content-box {
        width: 100%;

        .cad-box {
          position: relative;
          height: 100%;

          #loading-animation {
            position: absolute;
            top: 40%;
            left: 50%;
            z-index: 99;
          }

          .spinner {
            width: 50px;
            height: 50px;
            border: 5px solid rgba(209, 150, 150, 0.1);
            border-top-color: #3498db;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin: 0 auto;
          }

          .tips {
            font-family: PingFang SC;
            font-weight: 400;
            font-size: 11px;
            color: #e9ecf1;
            margin-top: 20px;
          }

          .map-container {
            width: 100%;
            height: 100%;
          }

          #map-container {
            width: 100%;
            height: 100%;
            position: absolute;
            left: 0;
            top: 0;
            background-color: #212830;
          }

          .direction {
            position: absolute;
            top: 15px;
            left: 20px;

            // .public-direction {
            //   position: absolute;
            //   font-family: SourceHanSerifSC;
            //   font-weight: 400;
            //   font-size: 10px;
            //   color: #b3b5b6;
            //   line-height: 9px;
            // }

            // .north {
            //   top: 3px;
            //   left: 50%;
            //   transform: translateX(-50%);
            // }

            // .east {
            //   top: 50%;
            //   right: 3px;
            //   transform: translateY(-50%);
            // }

            // .south {
            //   bottom: 4px;
            //   left: 50%;
            //   transform: translateX(-50%);
            // }

            // .west {
            //   top: 50%;
            //   left: 4px;
            //   transform: translateY(-50%);
            // }

            // .outer-ring {
            //   width: 97px;
            //   height: 97px;
            //   border-radius: 50%;
            //   background-color: rgba(72, 77, 84, 0.4);

            //   .inside-ring {
            //     width: 65px;
            //     height: 65px;
            //     border-radius: 50%;
            //     background-color: #212830;

            //     .img {
            //       width: 35px;
            //       height: 35px;
            //       margin-left: 10px;
            //       margin-top: -10px;
            //     }
            //   }
            // }
          }
        }
      }

      .property-box-in {
        height: 0px !important;
        overflow: hidden !important;
        transform: all 1s;
      }

      .property-box-out {
        position: absolute;
        right: 0;
        width: 273px;
        height: calc(100% - 74px);
        background: rgba(59, 68, 83, 0.4);
        border-radius: 4px;
        z-index: 1001;
        transform: all 1s;
        padding: 10px 15px;

        .group-box {
          height: 100%;
          overflow: auto;

          .group-wrap {
            display: flex;
            justify-content: flex-start;
            align-items: center;
            margin-top: 10px;

            .group-img {
              width: 15px;
              height: 15px;
            }

            .group-item {
              width: fit-content;
              margin-left: 6px;
              font-size: 11px;
            }
          }
        }

        .group-box::-webkit-scrollbar {
          width: 6px; /* 滚动条宽度 */
          height: 6px; /* 横向滚动条 */
        }

        .group-box::-webkit-scrollbar-thumb {
          background-color: #4a4a4a; /* 滚动条颜色 */
          border-radius: 4px;
        }

        .group-box::-webkit-scrollbar-track {
          background-color: #f0f0f0; /* 滚动条轨道背景 */
          border-radius: 4px;
        }

        .guide-box {
          position: absolute;
          left: 50%;
          bottom: 0;
          transform: translateX(-50%);
          width: 30%;
          height: 12px;
          background-color: #e0e0e0;
          display: flex;
          justify-content: center;
          align-items: center;
          z-index: 99;
          img {
            width: 25px;
            height: 10px;
          }
          img {
            transition: transform 0.3s ease;
          }

          img.rotated {
            transform: rotate(180deg);
          }
        }
      }
    }
  }
}

.fullName {
  font-size: 11px;
}

.panorama-body {
  width: 100%;
  height: 100%;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

.video-ma {
  width: 50px;
  height: 50px;
  position: absolute;
  z-index: 2;
  background-image: url('../assets/er.png');
  background-size: 100% 100%;
  right: 0;
}

.vid-humd-h {
  position: absolute;
  z-index: 2;
  right: 40px;
  top: 60px;
  color: rgb(44, 255, 2);
}

.vid-humd-s {
  position: absolute;
  z-index: 2;
  left: 240px;
  top: 110px;
  color: rgb(44, 255, 2);
}
</style>
<style scoped>
span {
  font-size: 11px;
  color: #f5f5f5;
}

.chart-box {
  width: 100%;
  height: 100%;
}

.asset-management {
  width: 100%;
  height: 100%;
  background-size: 100% 100%;
  box-sizing: border-box;
  display: flex;
  color: #fff;
  padding: 10px 10px;
}

.item-box {
  width: 100%;
  padding: 5px 5px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-wrap: nowrap;
  color: #f5f5f5;
}

.inp-box {
  width: 150px;
}

.data-box {
  width: 100%;
  height: 180px;
}

.text-box {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
}

.upload-bg /deep/ .upload-box {
  height: 200px;
}

.cad-device .panorama-body {
  display: flex;
  justify-content: flex-start;
}

.float-window-device-template {
  height: 500px;
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
}

.float-window-iframe-panorama-template {
  height: 500px;
}

.float-window-show-panorama {
  height: 500px;
}

.img-box {
  width: 230px;
  height: 160px;
  margin-right: 20px;
  position: relative;
  cursor: pointer;
  border: 2px solid transparent;
}

.checkbox {
  z-index: 999;
  position: absolute;
  top: 0px;
  right: 0px;
  width: 18px;
  height: 18px;
  background-color: #d2dee9;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
}
</style>
<!-- 合并能耗style代码 -->
<style>
.vdr {
  border: none !important;
}

.chart-container .delete-btn,
.chart-container .save-btn,
.chart-container .edit-btn,
.text-container .delete-btn,
.text-container .save-btn,
.text-container .edit-btn {
  border: none;
  cursor: pointer;
  z-index: 10;
}

.chart-container .delete-btn {
  position: absolute;
  top: 0px;
  right: 5px;
}

.chart-container .save-btn {
  position: absolute;
  top: 0px;
  right: 30px;
}

.chart-container .edit-btn {
  position: absolute;
  top: 0px;
  right: 55px;
}

.text-container .btn-actions {
  position: absolute;
  top: -10px;
  left: -20px;
}
.text-container .delete-btn {
  position: absolute;
  top: 0px;
  right: 10px;
}

.text-container .save-btn {
  position: absolute;
  top: 0px;
  right: 30px;
}

.text-container .edit-btn {
  position: absolute;
  top: 0px;
  right: 55px;
}

.text-container {
  display: inline-block; /* 根据内部文字撑开宽高 */
}
.draggable-text {
  margin: 0;
  white-space: nowrap; /* 防止换行，保证宽度跟文字走 */
}
.vjContextMenuItem {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  cursor: pointer;
  white-space: nowrap !important;
}

.vjContextMenuItemIcon {
  margin-left: 6px;
}

.vjContextMenuItemTitle {
  font-size: 11px !important;
  margin-left: 1px;
}

.vjContextMenuItem > *:nth-child(3) {
  display: none !important;
}

.img-marker,
.text-marker,
.text-control-marker,
.button-control-marker,
.video-control-marker {
  position: relative;
  display: inline-block;
}

.text-marker-close {
  position: absolute;
  top: -8px;
  right: -8px;
  background: red;
  color: white;
  font-weight: bold;
  width: 18px;
  height: 18px;
  text-align: center;
  line-height: 18px;
  border-radius: 50%;
  font-size: 15px;
  cursor: pointer;
  display: none;
  z-index: 10;
}

.img-marker-close {
  position: absolute;
  top: -8px;
  right: -8px;
  background: red;
  color: white;
  font-weight: bold;
  width: 18px;
  height: 18px;
  text-align: center;
  line-height: 14px;
  border-radius: 50%;
  font-size: 12px;
  cursor: pointer;
  display: none;
  z-index: 10;
}

.camera-marker-close {
  position: absolute;
  top: -8px;
  right: -8px;
  background: red;
  color: white;
  font-weight: bold;
  width: 18px;
  height: 18px;
  text-align: center;
  line-height: 18px;
  border-radius: 50%;
  font-size: 15px;
  cursor: pointer;
  display: none;
  z-index: 10;
}

.el-table tbody tr:hover > td {
  background-color: #3b4453 !important;
}

/* 表格背景色*/
.el-table,
.el-table__expanded-cell {
  background-color: transparent !important;
}

.el-table tr {
  background-color: transparent !important;
}

.el-table--enable-row-transition .el-table__body td,
.el-table .cell {
  background-color: transparent !important;
}

/* 去掉底部白线*/
.el-table::before {
  left: 0;
  bottom: 0;
  width: 100%;
  height: 0px;
}

/* 字体颜色*/
.el-table tr {
  background-color: transparent !important;
  color: #f0f0f0 !important;
  font-size: 11px !important;
}

.el-table__body tr,
.el-table__body td {
  border-left: 1px solid #383639 !important;
}

/* 右 边线 颜色*/
.el-table--border:after,
.el-table--group:after,
.el-table:before {
  background-color: transparent !important;
}

/* 四 边线 颜色*/
.el-table--border,
.el-table--group {
  border-color: transparent !important;
}

/**
  改变表格内行线颜色
 */
.el-table td,
.el-table th.is-leaf {
  border-bottom: 1px solid #383639 !important;
}

.el-table thead tr th {
  border-color: #3b4453 !important;
}

/*
改变表格内竖线颜色
*/
.el-table--border td,
.el-table--border th,
.el-table__body-wrapper .el-table--border.is-scrolling-left ~ .el-table__fixed {
  border-right: 1px solid #383639 !important;
}

/**
改变边框颜色
 */
.el-table--border,
.el-table--group {
  border: 1px solid transparent !important;
}

/**
  表格的行高
 */
.el-table__body tr,
.el-table__body td {
  padding: 0 !important;
  height: 30px !important;
}

.el-tree {
  background-color: transparent !important;
  color: #f0f0f0 !important;
}

.el-tree-node__label {
  font-size: 11px !important;
}

.el-tree-node__content:hover {
  background-color: #3b4453 !important;
}

.el-tree-node:focus > .el-tree-node__content {
  background-color: #3b4453;
}

.el-switch__label {
}

.el-switch__label * {
  font-size: 11px !important;
}

.el-tabs__item {
  font-size: 11px !important;
  color: #ffffff !important;
  width: 47%;
  text-align: center;
  height: 31px !important;
}

.el-tabs__item:hover {
  color: #1890ff !important;
}

.el-tabs__item.is-active {
  color: #1890ff !important;
}

.el-tabs__active-bar {
  height: 1px !important;
}

.el-tabs__nav-wrap::after {
  height: 1px !important;
  background-color: #383639;
}

.el-tabs__nav-scroll {
  width: 47vw !important;
}

.el-calendar-table .el-calendar-day {
  height: 27px !important;
}

.el-calendar {
  background-color: transparent !important;
}

.el-calendar-table .el-calendar-day:hover {
  background-color: #2c3440 !important;
}

.el-calendar-table td.is-selected {
  background-color: #2c3440 !important;
}

.el-calendar__title {
  color: #ffffff;
}

.el-calendar__header {
  padding: 3px 0px;
  border-bottom: 1px solid transparent;
  display: none;
}

.el-calendar-table thead th {
  text-align: center;
  color: #ffffff;
}

.el-calendar-table tr:first-child td {
  border-top: 1px solid #383639;
}

.el-calendar-table td {
  border-bottom: 1px solid #383639;
  border-right: 1px solid #383639;
}

.el-calendar-table tr td:first-child {
  border-left: 1px solid #383639;
}

.el-calendar-table:not(.is-range) td.prev,
.el-calendar-table:not(.is-range) td.next {
  color: #505050;
}

.el-calendar-table thead th {
  border: 1px solid #383639;
}

.el-calendar__body {
  padding: 0px 0px 0px !important;
}

.el-calendar__button-group {
  display: none;
}

.el-calendar-table thead th {
  padding: 7px 0 !important;
}

.el-input__inner {
  font-size: 11px !important;
}

.ba {
  color: #3b44534b;
}

.table-ll {
  height: 500px;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.table-ll::-webkit-scrollbar {
  width: 8px; /* 设置滚动条宽度 */
}

.table-ll::-webkit-scrollbar-track {
  background: #252526; /* 轨道颜色 */
}

.table-ll::-webkit-scrollbar-thumb {
  background-color: #252526; /* 滑块颜色 */
  border-radius: 4px;
}

.table-ll::-webkit-scrollbar-thumb:hover {
  background-color: #252526; /* 悬停颜色 */
}
</style>
<style scoped>
/deep/ .el-switch {
  width: 27px;
  height: 15px;
}

/deep/ .el-switch__core {
  height: 13px;
}

/deep/ .el-switch__core:after {
  top: 0;
  width: 11px;
  height: 11px;
}

/deep/.el-switch.is-checked .el-switch__core::after {
  left: 122%;
  margin-left: -17px;
}

/deep/.el-switch__core:after {
  background-color: #535353;
}
</style>
<style scoped>
.custom-menu {
  position: absolute;
  z-index: 1000;
  background: white;
  width: 130px;
  border: 1px solid #000000;
  box-shadow: 1px 1px 10px #000;
  background: rgb(37, 37, 38);
  border-radius: 3px;
  padding: 8px 0;
  color: #ccccc1;
}

.custom-menu ul {
  margin: 0;
  list-style: none;
  background: rgb(37, 37, 38);
}

.custom-menu li {
  padding: 8px 12px;
  cursor: pointer;
  color: #ccccc1;
}

.custom-menu li:hover {
  background-color: #195e86;
}

.config-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}

.angleViewBox {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 300px;
  background-color: lightblue;
}

.draggable-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  user-select: none;
}

.btn-actions {
  position: absolute;
  top: -10px;
  right: -10px;
  display: flex;
  gap: 5px;
}

.btn-actions i {
  background: #fff;
  border: 1px solid #999;
  border-radius: 50%;
  font-size: 12px;
  padding: 2px;
  cursor: pointer;
  color: #000000;
}

.draggable-btn.is-hover:hover {
  opacity: 0.9;
  box-shadow: 0 0 5px #409eff;
}

.time-chart-icon {
  width: 30px;
  height: 30px;
  margin-right: 5px;
}

.time-chart-item {
  position: absolute;
}
</style>
